|
73 | 73 | _GetQueuedCompletionStatus,
|
74 | 74 | _GetStdHandle,
|
75 | 75 | _GetSystemInfo,
|
| 76 | + _GetSystemTimeAsFileTime, |
76 | 77 | _GetThreadContext,
|
77 | 78 | _LoadLibraryW,
|
78 | 79 | _LoadLibraryA,
|
| 80 | + _QueryPerformanceCounter, |
| 81 | + _QueryPerformanceFrequency, |
79 | 82 | _ResumeThread,
|
80 | 83 | _SetConsoleCtrlHandler,
|
81 | 84 | _SetErrorMode,
|
@@ -188,6 +191,11 @@ func loadOptionalSyscalls() {
|
188 | 191 | throw("ntdll.dll not found")
|
189 | 192 | }
|
190 | 193 | _NtWaitForSingleObject = windowsFindfunc(n32, []byte("NtWaitForSingleObject\000"))
|
| 194 | + |
| 195 | + if windowsFindfunc(n32, []byte("wine_get_version\000")) != nil { |
| 196 | + // running on Wine |
| 197 | + initWine(k32) |
| 198 | + } |
191 | 199 | }
|
192 | 200 |
|
193 | 201 | //go:nosplit
|
@@ -292,6 +300,79 @@ func osinit() {
|
292 | 300 | stdcall2(_SetProcessPriorityBoost, currentProcess, 1)
|
293 | 301 | }
|
294 | 302 |
|
| 303 | +func nanotime() int64 |
| 304 | + |
| 305 | +// useQPCTime controls whether time.now and nanotime use QueryPerformanceCounter. |
| 306 | +// This is only set to 1 when running under Wine. |
| 307 | +var useQPCTime uint8 |
| 308 | + |
| 309 | +var qpcStartCounter int64 |
| 310 | +var qpcMultiplier int64 |
| 311 | + |
| 312 | +//go:nosplit |
| 313 | +func nanotimeQPC() int64 { |
| 314 | + var counter int64 = 0 |
| 315 | + stdcall1(_QueryPerformanceCounter, uintptr(unsafe.Pointer(&counter))) |
| 316 | + |
| 317 | + // returns number of nanoseconds |
| 318 | + return (counter - qpcStartCounter) * qpcMultiplier |
| 319 | +} |
| 320 | + |
| 321 | +//go:nosplit |
| 322 | +func nowQPC() (sec int64, nsec int32, mono int64) { |
| 323 | + var ft int64 |
| 324 | + stdcall1(_GetSystemTimeAsFileTime, uintptr(unsafe.Pointer(&ft))) |
| 325 | + |
| 326 | + t := (ft - 116444736000000000) * 100 |
| 327 | + |
| 328 | + sec = t / 1000000000 |
| 329 | + nsec = int32(t - sec*1000000000) |
| 330 | + |
| 331 | + mono = nanotimeQPC() |
| 332 | + return |
| 333 | +} |
| 334 | + |
| 335 | +func initWine(k32 uintptr) { |
| 336 | + _GetSystemTimeAsFileTime = windowsFindfunc(k32, []byte("GetSystemTimeAsFileTime\000")) |
| 337 | + if _GetSystemTimeAsFileTime == nil { |
| 338 | + throw("could not find GetSystemTimeAsFileTime() syscall") |
| 339 | + } |
| 340 | + |
| 341 | + _QueryPerformanceCounter = windowsFindfunc(k32, []byte("QueryPerformanceCounter\000")) |
| 342 | + _QueryPerformanceFrequency = windowsFindfunc(k32, []byte("QueryPerformanceFrequency\000")) |
| 343 | + if _QueryPerformanceCounter == nil || _QueryPerformanceFrequency == nil { |
| 344 | + throw("could not find QPC syscalls") |
| 345 | + } |
| 346 | + |
| 347 | + // We can not simply fallback to GetSystemTimeAsFileTime() syscall, since its time is not monotonic, |
| 348 | + // instead we use QueryPerformanceCounter family of syscalls to implement monotonic timer |
| 349 | + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx |
| 350 | + |
| 351 | + var tmp int64 |
| 352 | + stdcall1(_QueryPerformanceFrequency, uintptr(unsafe.Pointer(&tmp))) |
| 353 | + if tmp == 0 { |
| 354 | + throw("QueryPerformanceFrequency syscall returned zero, running on unsupported hardware") |
| 355 | + } |
| 356 | + |
| 357 | + // This should not overflow, it is a number of ticks of the performance counter per second, |
| 358 | + // its resolution is at most 10 per usecond (on Wine, even smaller on real hardware), so it will be at most 10 millions here, |
| 359 | + // panic if overflows. |
| 360 | + if tmp > (1<<31 - 1) { |
| 361 | + throw("QueryPerformanceFrequency overflow 32 bit divider, check nosplit discussion to proceed") |
| 362 | + } |
| 363 | + qpcFrequency := int32(tmp) |
| 364 | + stdcall1(_QueryPerformanceCounter, uintptr(unsafe.Pointer(&qpcStartCounter))) |
| 365 | + |
| 366 | + // Since we are supposed to run this time calls only on Wine, it does not lose precision, |
| 367 | + // since Wine's timer is kind of emulated at 10 Mhz, so it will be a nice round multiplier of 100 |
| 368 | + // but for general purpose system (like 3.3 Mhz timer on i7) it will not be very precise. |
| 369 | + // We have to do it this way (or similar), since multiplying QPC counter by 100 millions overflows |
| 370 | + // int64 and resulted time will always be invalid. |
| 371 | + qpcMultiplier = int64(timediv(1000000000, qpcFrequency, nil)) |
| 372 | + |
| 373 | + useQPCTime = 1 |
| 374 | +} |
| 375 | + |
295 | 376 | //go:nosplit
|
296 | 377 | func getRandomData(r []byte) {
|
297 | 378 | n := 0
|
@@ -578,8 +659,6 @@ func unminit() {
|
578 | 659 | *tp = 0
|
579 | 660 | }
|
580 | 661 |
|
581 |
| -func nanotime() int64 |
582 |
| - |
583 | 662 | // Calling stdcall on os stack.
|
584 | 663 | // May run during STW, so write barriers are not allowed.
|
585 | 664 | //go:nowritebarrier
|
|
0 commit comments