Skip to content

Commit 74b62b4

Browse files
committed
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
1 parent 8ee2a66 commit 74b62b4

File tree

2 files changed

+55
-13
lines changed

2 files changed

+55
-13
lines changed

src/pkg/runtime/os_windows.c

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#pragma dynimport runtime·GetProcAddress GetProcAddress "kernel32.dll"
2424
#pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll"
2525
#pragma dynimport runtime·GetSystemInfo GetSystemInfo "kernel32.dll"
26-
#pragma dynimport runtime·GetSystemTimeAsFileTime GetSystemTimeAsFileTime "kernel32.dll"
2726
#pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll"
2827
#pragma dynimport runtime·LoadLibrary LoadLibraryW "kernel32.dll"
2928
#pragma dynimport runtime·LoadLibraryA LoadLibraryA "kernel32.dll"
@@ -55,7 +54,6 @@ extern void *runtime·GetEnvironmentStringsW;
5554
extern void *runtime·GetProcAddress;
5655
extern void *runtime·GetStdHandle;
5756
extern void *runtime·GetSystemInfo;
58-
extern void *runtime·GetSystemTimeAsFileTime;
5957
extern void *runtime·GetThreadContext;
6058
extern void *runtime·LoadLibrary;
6159
extern void *runtime·LoadLibraryA;
@@ -265,25 +263,53 @@ runtime·unminit(void)
265263
{
266264
}
267265

266+
// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
267+
typedef struct KSYSTEM_TIME {
268+
uint32 LowPart;
269+
int32 High1Time;
270+
int32 High2Time;
271+
} KSYSTEM_TIME;
272+
273+
const KSYSTEM_TIME* INTERRUPT_TIME = (KSYSTEM_TIME*)0x7ffe0008;
274+
const KSYSTEM_TIME* SYSTEM_TIME = (KSYSTEM_TIME*)0x7ffe0014;
275+
268276
#pragma textflag NOSPLIT
269277
int64
270-
runtime·nanotime(void)
278+
runtime·systime(KSYSTEM_TIME *timeaddr)
271279
{
272-
int64 filetime;
273-
274-
runtime·stdcall(runtime·GetSystemTimeAsFileTime, 1, &filetime);
280+
KSYSTEM_TIME t;
281+
int32 i;
282+
283+
for(i = 0; i < 10000; i++) {
284+
// these fields must be read in that order (see URL above)
285+
t.High1Time = timeaddr->High1Time;
286+
t.LowPart = timeaddr->LowPart;
287+
t.High2Time = timeaddr->High2Time;
288+
if(t.High1Time == t.High2Time)
289+
return (int64)t.High1Time<<32 | t.LowPart;
290+
if((i%100) == 0)
291+
runtime·osyield();
292+
}
293+
runtime·throw("interrupt/system time is changing too fast");
294+
return 0;
295+
}
275296

276-
// Filetime is 100s of nanoseconds since January 1, 1601.
277-
// Convert to nanoseconds since January 1, 1970.
278-
return (filetime - 116444736000000000LL) * 100LL;
297+
#pragma textflag NOSPLIT
298+
int64
299+
runtime·nanotime(void)
300+
{
301+
return runtime·systime(INTERRUPT_TIME) * 100LL;
279302
}
280303

281304
void
282305
time·now(int64 sec, int32 usec)
283306
{
284307
int64 ns;
285308

286-
ns = runtime·nanotime();
309+
// SystemTime is 100s of nanoseconds since January 1, 1601.
310+
// Convert to nanoseconds since January 1, 1970.
311+
ns = (runtime·systime(SYSTEM_TIME) - 116444736000000000LL) * 100LL;
312+
287313
sec = ns / 1000000000LL;
288314
usec = ns - sec * 1000000000LL;
289315
FLUSH(&sec);

src/pkg/time/sleep_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ import (
1515
. "time"
1616
)
1717

18+
// Go runtime uses different Windows timers for time.Now and sleeping.
19+
// These can tick at different frequencies and can arrive out of sync.
20+
// The effect can be seen, for example, as time.Sleep(100ms) is actually
21+
// shorter then 100ms when measured as difference between time.Now before and
22+
// after time.Sleep call. This was observed on Windows XP SP3 (windows/386).
23+
// windowsInaccuracy is to ignore such errors.
24+
const windowsInaccuracy = 17 * Millisecond
25+
1826
func TestSleep(t *testing.T) {
1927
const delay = 100 * Millisecond
2028
go func() {
@@ -23,8 +31,12 @@ func TestSleep(t *testing.T) {
2331
}()
2432
start := Now()
2533
Sleep(delay)
34+
delayadj := delay
35+
if runtime.GOOS == "windows" {
36+
delayadj -= windowsInaccuracy
37+
}
2638
duration := Now().Sub(start)
27-
if duration < delay {
39+
if duration < delayadj {
2840
t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
2941
}
3042
}
@@ -150,10 +162,14 @@ func TestAfter(t *testing.T) {
150162
const delay = 100 * Millisecond
151163
start := Now()
152164
end := <-After(delay)
153-
if duration := Now().Sub(start); duration < delay {
165+
delayadj := delay
166+
if runtime.GOOS == "windows" {
167+
delayadj -= windowsInaccuracy
168+
}
169+
if duration := Now().Sub(start); duration < delayadj {
154170
t.Fatalf("After(%s) slept for only %d ns", delay, duration)
155171
}
156-
if min := start.Add(delay); end.Before(min) {
172+
if min := start.Add(delayadj); end.Before(min) {
157173
t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
158174
}
159175
}

0 commit comments

Comments
 (0)