[PATCH 0/2] MR9875: Implement InterruptTimeBias and use it in RtlQueryUnbiasedInterruptTime.
After https://gitlab.winehq.org/wine/wine/-/merge_requests/8916 `RtlQueryUnbiasedInterruptTime` now includes suspend time as well when it shouldn't. This fixes this and also allows other components like winevulkan to use `RtlQueryUnbiasedInterruptTime`, if that kind of tick count is necessary there. Since calculating the bias is relatively expensive, this is done together with the timezone bias update every second in such a way that it can only monotonically increase. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9875
From: Marc-Aurel Zent <mzent@codeweavers.com> --- dlls/ntdll/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ntdll/time.c b/dlls/ntdll/time.c index 105a6cf5bc8..187e258c098 100644 --- a/dlls/ntdll/time.c +++ b/dlls/ntdll/time.c @@ -476,7 +476,7 @@ BOOL WINAPI RtlQueryUnbiasedInterruptTime(ULONGLONG *time) low = user_shared_data->InterruptTime.LowPart; } while (high != user_shared_data->InterruptTime.High2Time); - /* FIXME: should probably subtract InterruptTimeBias */ *time = (ULONGLONG)high << 32 | low; + *time -= user_shared_data->InterruptTimeBias; return TRUE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9875
From: Marc-Aurel Zent <mzent@codeweavers.com> --- server/fd.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/server/fd.c b/server/fd.c index f70bec354a3..0dbefc7cfc6 100644 --- a/server/fd.c +++ b/server/fd.c @@ -36,6 +36,9 @@ #ifdef HAVE_LINUX_MAJOR_H #include <linux/major.h> #endif +#ifdef __APPLE__ +#include <mach/mach_time.h> +#endif #ifdef HAVE_SYS_STATVFS_H #include <sys/statvfs.h> #endif @@ -377,11 +380,44 @@ static void atomic_store_long(volatile LONG *ptr, LONG value) #endif } +static void atomic_store_ulonglong(volatile ULONGLONG *ptr, ULONGLONG value) +{ +#if defined(__i386__) || defined(__x86_64__) + *ptr = value; +#else + __atomic_store_n(ptr, value, __ATOMIC_SEQ_CST); +#endif +} + +static timeout_t calculate_suspend_bias( timeout_t last_bias ) +{ + timeout_t counter = 0; + timeout_t unbiased_counter = 0; +#ifdef __APPLE__ + static mach_timebase_info_data_t timebase; + + if (!timebase.denom) mach_timebase_info( &timebase ); + unbiased_counter = mach_absolute_time() * timebase.numer / timebase.denom / 100; + counter = mach_continuous_time() * timebase.numer / timebase.denom / 100; +#elif defined(HAVE_CLOCK_GETTIME) + struct timespec ts; + if (!clock_gettime( CLOCK_MONOTONIC, &ts )) + counter = unbiased_counter = (timeout_t)ts.tv_sec * TICKS_PER_SEC + ts.tv_nsec / 100; +#ifdef CLOCK_BOOTTIME + if (!clock_gettime( CLOCK_BOOTTIME, &ts )) + counter = (timeout_t)ts.tv_sec * TICKS_PER_SEC + ts.tv_nsec / 100; +#endif +#endif + if (unbiased_counter) + return max( counter - unbiased_counter, last_bias ); + return last_bias; +} + static void set_user_shared_data_time(void) { timeout_t tick_count = monotonic_time / 10000; static timeout_t last_timezone_update; - timeout_t timezone_bias; + timeout_t timezone_bias, suspend_bias; struct tm *tm; time_t now; @@ -393,10 +429,12 @@ static void set_user_shared_data_time(void) tm = localtime( &now ); if (tm->tm_isdst) timezone_bias -= 3600; timezone_bias *= TICKS_PER_SEC; + suspend_bias = calculate_suspend_bias( user_shared_data->InterruptTimeBias ); atomic_store_long(&user_shared_data->TimeZoneBias.High2Time, timezone_bias >> 32); atomic_store_ulong(&user_shared_data->TimeZoneBias.LowPart, timezone_bias); atomic_store_long(&user_shared_data->TimeZoneBias.High1Time, timezone_bias >> 32); + atomic_store_ulonglong(&user_shared_data->InterruptTimeBias, suspend_bias); last_timezone_update = monotonic_time; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9875
I am afraid this is not a reliable way to compute the unbiased time. Random factors may influence this and it looks challenging (even if possible) to implement a reliable bias. IMO if we have to implement that we'd be better off detecting suspend in some way and use our own current bias always, which, even if not 100% correct, would at least be consistent over time and would not introduce random time jumps. Also note that, as Rémi noted in the linked MR, that an ability to use RtlQueryUnbiasedInterruptTime() doesn't help winevulkan: it has to convert between CLOCK_MONOTONIC or CLOCK_MONOTONIC_RAW and QPC time, it is not free to choose unibiased QPC time instead, that is not what apps may expect. Unless we are known to have something depending on CLOCK_BOOTTIME now, I'd also suggest to revert MR 8916 for now and help both this aspect with now wrong unbiased time and winevulkan. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9875#note_126636
On Mon Jan 12 17:10:32 2026 +0000, Paul Gofman wrote:
I am afraid this is not a reliable way to compute the unbiased time. Random factors may influence this and it looks challenging (even if possible) to implement a reliable bias. IMO if we have to implement that we'd be better off detecting suspend in some way and use our own current bias always, which, even if not 100% correct, would at least be consistent over time and would not introduce random time jumps. Also note that, as Rémi noted in the linked MR, that an ability to use RtlQueryUnbiasedInterruptTime() doesn't help winevulkan: it has to convert between CLOCK_MONOTONIC or CLOCK_MONOTONIC_RAW and QPC time, it is not free to choose unibiased QPC time instead, that is not what apps may expect. Unless we are known to have something depending on CLOCK_BOOTTIME now, I'd also suggest to revert MR 8916 for now and help both this aspect with now wrong unbiased time and winevulkan. I was having an issue with a proprietary modern dotnet application that mixed tick count and system time calculation and hung up if the two diverged too much...
If this is indeed an unsolvable problem for winevulkan at the moment, then maybe this is worth reverting, although unless I am misunderstanding something `InterruptTimeBias` is now exactly what is needed to convert between CLOCK_MONOTONIC and QPC time; and I don't believe there is a much more accurate way to implement this otherwise (and in practice this also does not fluctuate at all really after a few iterations of taking the maximum of CLOCK_BOOTTIME - CLOCK_MONOTONIC without suspending the system). Of note is also that on macOS tick counts have included suspend counts for quite a while so the winevulkan issue this is currently causing has been present there for quite a while (and it would be nice if it could be fixed as well). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9875#note_126637
On Mon Jan 12 17:11:37 2026 +0000, Marc-Aurel Zent wrote: > I was having an issue with a proprietary modern dotnet application that > mixed tick count and system time calculation and hung up if the two > diverged too much... > If this is indeed an unsolvable problem for winevulkan at the moment, > then maybe this is worth reverting, although unless I am > misunderstanding something `InterruptTimeBias` is now exactly what is > needed to convert between CLOCK_MONOTONIC and QPC time; and I don't > believe there is a much more accurate way to implement this otherwise > (and in practice this also does not fluctuate at all really after a few > iterations of taking the maximum of CLOCK_BOOTTIME - CLOCK_MONOTONIC > without suspending the system). > Of note is also that on macOS tick counts have included suspend ticks > for quite a while so the winevulkan issue this is currently causing has > been present there for years (and it would be nice if it could be fixed > as well). Perhaps winevulkan could use InterruptTimeBias. But computing that discretely (not synced with actual current time reported), and doing it this way is probably not an option, that is too inaccurate and subject to random jumps and that is going to affect not only vulkan but also system time queries and user shared data info. Maybe a more robust way would be something like: - use CLOCK_MONOTONIC for Qpc as a source and add InterruptTimeBias there; - try to detect time jumps in wineserver based on the difference between the CLOCK_MONOTONIC and CLOCK_BOOTTIME, if that jumped (compared to current bias) over some threshold and that is consistent over a few consequent time update then update the time bias. This is also not ideal of course as the bias will be wrong at first time queries right after reboot. Also, there are audio backends which could potentially also got the time mismatch now, and might need the same adjustment too. IMO the bottom line of that is that is not so simple change in any case, and to fix the regressions introduced by the change (even while the change itself might be correct) reverting is probably best for code freeze at least. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9875#note_126638
IMO the bottom line of that is that is not so simple change in any case, and to fix the regressions introduced by the change (even while the change itself might be correct) reverting is probably best for code freeze at least.
Are there reports somewhere of such regressions? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9875#note_126639
On Mon Jan 12 17:49:14 2026 +0000, Alexandre Julliard wrote:
IMO the bottom line of that is that is not so simple change in any case, and to fix the regressions introduced by the change (even while the change itself might be correct) reverting is probably best for code freeze at least. Are there reports somewhere of such regressions? Not that I know of. It is just that wrong QPC to Vulkan time conversions looks very likely to cause some. While in the wild it might be needed to suspend computer to reproduce that and stomp on a particular game which depends on that.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9875#note_126640
Of note is also that on macOS tick counts have included suspend ticks for quite a while so the winevulkan issue this is currently causing has been present there for years (and it would be nice if it could be fixed as well).
Calibrated timestamps is not implemented for mac os -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9875#note_126663
participants (5)
-
Alexandre Julliard (@julliard) -
Etaash Mathamsetty (@etaash.mathamsetty) -
Marc-Aurel Zent -
Marc-Aurel Zent (@mzent) -
Paul Gofman (@gofman)