mirror of
https://github.com/golang/go
synced 2024-11-19 06:04:39 -07:00
runtime: implement monotonic clocks on windows
Update #6007. LGTM=minux, dvyukov R=golang-codereviews, dvyukov, patrick, aram.h, minux CC=golang-codereviews https://golang.org/cl/108700045
This commit is contained in:
parent
8ee2a6658b
commit
74b62b4864
@ -23,7 +23,6 @@
|
||||
#pragma dynimport runtime·GetProcAddress GetProcAddress "kernel32.dll"
|
||||
#pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll"
|
||||
#pragma dynimport runtime·GetSystemInfo GetSystemInfo "kernel32.dll"
|
||||
#pragma dynimport runtime·GetSystemTimeAsFileTime GetSystemTimeAsFileTime "kernel32.dll"
|
||||
#pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll"
|
||||
#pragma dynimport runtime·LoadLibrary LoadLibraryW "kernel32.dll"
|
||||
#pragma dynimport runtime·LoadLibraryA LoadLibraryA "kernel32.dll"
|
||||
@ -55,7 +54,6 @@ extern void *runtime·GetEnvironmentStringsW;
|
||||
extern void *runtime·GetProcAddress;
|
||||
extern void *runtime·GetStdHandle;
|
||||
extern void *runtime·GetSystemInfo;
|
||||
extern void *runtime·GetSystemTimeAsFileTime;
|
||||
extern void *runtime·GetThreadContext;
|
||||
extern void *runtime·LoadLibrary;
|
||||
extern void *runtime·LoadLibraryA;
|
||||
@ -265,17 +263,42 @@ runtime·unminit(void)
|
||||
{
|
||||
}
|
||||
|
||||
// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
|
||||
typedef struct KSYSTEM_TIME {
|
||||
uint32 LowPart;
|
||||
int32 High1Time;
|
||||
int32 High2Time;
|
||||
} KSYSTEM_TIME;
|
||||
|
||||
const KSYSTEM_TIME* INTERRUPT_TIME = (KSYSTEM_TIME*)0x7ffe0008;
|
||||
const KSYSTEM_TIME* SYSTEM_TIME = (KSYSTEM_TIME*)0x7ffe0014;
|
||||
|
||||
#pragma textflag NOSPLIT
|
||||
int64
|
||||
runtime·systime(KSYSTEM_TIME *timeaddr)
|
||||
{
|
||||
KSYSTEM_TIME t;
|
||||
int32 i;
|
||||
|
||||
for(i = 0; i < 10000; i++) {
|
||||
// these fields must be read in that order (see URL above)
|
||||
t.High1Time = timeaddr->High1Time;
|
||||
t.LowPart = timeaddr->LowPart;
|
||||
t.High2Time = timeaddr->High2Time;
|
||||
if(t.High1Time == t.High2Time)
|
||||
return (int64)t.High1Time<<32 | t.LowPart;
|
||||
if((i%100) == 0)
|
||||
runtime·osyield();
|
||||
}
|
||||
runtime·throw("interrupt/system time is changing too fast");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma textflag NOSPLIT
|
||||
int64
|
||||
runtime·nanotime(void)
|
||||
{
|
||||
int64 filetime;
|
||||
|
||||
runtime·stdcall(runtime·GetSystemTimeAsFileTime, 1, &filetime);
|
||||
|
||||
// Filetime is 100s of nanoseconds since January 1, 1601.
|
||||
// Convert to nanoseconds since January 1, 1970.
|
||||
return (filetime - 116444736000000000LL) * 100LL;
|
||||
return runtime·systime(INTERRUPT_TIME) * 100LL;
|
||||
}
|
||||
|
||||
void
|
||||
@ -283,7 +306,10 @@ time·now(int64 sec, int32 usec)
|
||||
{
|
||||
int64 ns;
|
||||
|
||||
ns = runtime·nanotime();
|
||||
// SystemTime is 100s of nanoseconds since January 1, 1601.
|
||||
// Convert to nanoseconds since January 1, 1970.
|
||||
ns = (runtime·systime(SYSTEM_TIME) - 116444736000000000LL) * 100LL;
|
||||
|
||||
sec = ns / 1000000000LL;
|
||||
usec = ns - sec * 1000000000LL;
|
||||
FLUSH(&sec);
|
||||
|
@ -15,6 +15,14 @@ import (
|
||||
. "time"
|
||||
)
|
||||
|
||||
// Go runtime uses different Windows timers for time.Now and sleeping.
|
||||
// These can tick at different frequencies and can arrive out of sync.
|
||||
// The effect can be seen, for example, as time.Sleep(100ms) is actually
|
||||
// shorter then 100ms when measured as difference between time.Now before and
|
||||
// after time.Sleep call. This was observed on Windows XP SP3 (windows/386).
|
||||
// windowsInaccuracy is to ignore such errors.
|
||||
const windowsInaccuracy = 17 * Millisecond
|
||||
|
||||
func TestSleep(t *testing.T) {
|
||||
const delay = 100 * Millisecond
|
||||
go func() {
|
||||
@ -23,8 +31,12 @@ func TestSleep(t *testing.T) {
|
||||
}()
|
||||
start := Now()
|
||||
Sleep(delay)
|
||||
delayadj := delay
|
||||
if runtime.GOOS == "windows" {
|
||||
delayadj -= windowsInaccuracy
|
||||
}
|
||||
duration := Now().Sub(start)
|
||||
if duration < delay {
|
||||
if duration < delayadj {
|
||||
t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
|
||||
}
|
||||
}
|
||||
@ -150,10 +162,14 @@ func TestAfter(t *testing.T) {
|
||||
const delay = 100 * Millisecond
|
||||
start := Now()
|
||||
end := <-After(delay)
|
||||
if duration := Now().Sub(start); duration < delay {
|
||||
delayadj := delay
|
||||
if runtime.GOOS == "windows" {
|
||||
delayadj -= windowsInaccuracy
|
||||
}
|
||||
if duration := Now().Sub(start); duration < delayadj {
|
||||
t.Fatalf("After(%s) slept for only %d ns", delay, duration)
|
||||
}
|
||||
if min := start.Add(delay); end.Before(min) {
|
||||
if min := start.Add(delayadj); end.Before(min) {
|
||||
t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user