After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 748327 - W32: GDK can be hypothetically stuck in time
W32: GDK can be hypothetically stuck in time
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Backend: Win32
unspecified
Other All
: Normal normal
: ---
Assigned To: gtk-win32 maintainers
gtk-bugs
Depends on:
Blocks:
 
 
Reported: 2015-04-22 19:31 UTC by LRN
Modified: 2016-01-18 09:30 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
W32: Detect ticks wraparound (1.03 KB, patch)
2015-04-22 19:31 UTC, LRN
committed Details | Review

Description LRN 2015-04-22 19:31:17 UTC
Ticks do wrap around, GDK shouldn't expect their value to grow indefinitely.
Comment 1 LRN 2015-04-22 19:31:23 UTC
Created attachment 302180 [details] [review]
W32: Detect ticks wraparound
Comment 2 Ignacio Casal Quinteiro (nacho) 2015-04-23 06:43:27 UTC
Review of attachment 302180 [details] [review]:

See comments.

::: gdk/win32/gdkevents-win32.c
@@ +176,2 @@
   if (suggested_tick == 0)
     suggested_tick = GetTickCount ();

leave empty line between different ifs. I know it was like that before but anyway.

@@ +178,3 @@
+  /* Ticks eventually wrap around.
+   * This works as long as the interval between ticks is < 2147483648ms */
+  if (suggested_tick <= cur_tick && ((cur_tick - suggested_tick) < 0x7FFFFFFF))

after reading this comment and the code I do still do not understand it. Can you please specify a bit more in the comment?
Comment 3 LRN 2015-04-23 09:17:32 UTC
_gdk_win32_get_next_tick() is called with a reported timestamp most of the time (and only once - with 0, initially).

Originally code checked reported timestamp (suggested_tick) and returned statically stored previous result if suggested_tick was lower (to avoid jumping back in time).

However, ticks *do* jump back in time once the variable overflows. I modified the check to detect such big jumps and don't consider them erroneous. If that is not done, this function will keep returning the last forward-time timestamp it got indefinitely.

I should probably also initialize our_tick to GetTickCount() too.
Also, i should probably switch from 0 in the initial call to GetMessageTime().
Comment 4 Alexander Larsson 2015-04-27 15:08:45 UTC
Review of attachment 302180 [details] [review]:

::: gdk/win32/gdkevents-win32.c
@@ +178,3 @@
+  /* Ticks eventually wrap around.
+   * This works as long as the interval between ticks is < 2147483648ms */
+  if (suggested_tick <= cur_tick && ((cur_tick - suggested_tick) < 0x7FFFFFFF))

This does not look right. Say we're overflowing and cur_tick is MAXINT, and the new suggested_tick is 1, then suggested_tick <= cur_tick, and (cur_tick - suggested_tick) is (MAXINT - 1), which is > 0x7fffffff. So, we will return cur_tick even thought we *should* wrap.

Shouldn't this just be something like the macro in the X11 code:
#define XSERVER_TIME_IS_LATER(time1, time2)                        \
  ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) ||  \
    (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 ))     \
Comment 5 LRN 2015-04-27 15:48:39 UTC
(In reply to Alexander Larsson from comment #4)
> Review of attachment 302180 [details] [review] [review]:
> 
> ::: gdk/win32/gdkevents-win32.c
> @@ +178,3 @@
> +  /* Ticks eventually wrap around.
> +   * This works as long as the interval between ticks is < 2147483648ms */
> +  if (suggested_tick <= cur_tick && ((cur_tick - suggested_tick) <
> 0x7FFFFFFF))
> 
> This does not look right. Say we're overflowing and cur_tick is MAXINT, and
> the new suggested_tick is 1, then suggested_tick <= cur_tick, and (cur_tick
> - suggested_tick) is (MAXINT - 1), which is > 0x7fffffff. So, we will return
> cur_tick even thought we *should* wrap.
> 
> Shouldn't this just be something like the macro in the X11 code:
> #define XSERVER_TIME_IS_LATER(time1, time2)                        \
>   ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) ||  \
>     (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 ))     \

Um, okay, let's go at this again. Here's the code:

>  if (suggested_tick <= cur_tick && ((cur_tick - suggested_tick) < 0x7FFFFFFF))
>    return cur_tick;
>  else
>    return cur_tick = suggested_tick;

Ticks wrapped, which means that cur_tick is 0xFFFFFFFF (or nearly as much), and suggested_tick is 0 or 1 or some kind of small integer.

(suggested_tick <= cur_tick) is TRUE
(cur_tick - suggested_tick) is (0xFFFFFFFF-or-almost-it minus some-kind-of-small-integer), and is also almost 0xFFFFFFFF.
(0xFFFFFFFF < 0x7FFFFFFF) would be FALSE in that case, and the second branch, which uses suggested_tick (and makes it the new cur_tick), will be chosen.

I don't see anything wrong here.

I've considered other ways of testing for wraps, and this is the sanest i could come up with.
Comment 6 LRN 2015-04-27 20:20:12 UTC
Removing the dependency on bug 748325. Damn, why do i always use this feature and always get it so *wrong*?
Comment 7 Alexander Larsson 2015-11-30 13:34:36 UTC
Yeah, you're right. The only problem i can see with your patch is if suggested_tick happens to be 0 during a wrap, but in that case we should be calling GetTickCount and get back something like 0, which is probably ok.
Comment 8 Alexander Larsson 2015-11-30 13:35:10 UTC
Review of attachment 302180 [details] [review]:

please commit
Comment 9 LRN 2015-11-30 16:04:40 UTC
Now that i can push this, i'm starting to have doubts. While in theory this code looks correct, i haven't tested some of its branches. That is, i ran GTK apps with this patch applied for some time, and it works OK in normal circumstances, but i haven't had a chance to see how it handles the actual wraparound (i.e. run a GTK app shortly before GetTickCount() wraps, observe its reaction to the wraparound). I will try to test it (it takes 49.7 days to achieve wraparound).

How does GTK/GDK handle this on other platforms, by the way? I've noticed that GdkEvent time fields are generally spec'ed as guint32, which means they have the same problem.
Comment 10 Alexander Larsson 2015-12-01 10:35:58 UTC
xserver event times do indeed wrap around, so, the X11 backend uses the above XSERVER_TIME_IS_LATER every time it compares two timestamps.

Does this handle *every* case anyone ever compares to timestamps? Who knows...
We certainly don't have any test case for this.
Comment 11 LRN 2016-01-18 09:30:39 UTC
Sorry, i blew the test (was asleep). I'll commit this now, and maybe
test it some other time.

Attachment 302180 [details] pushed as f74f81f - W32: Detect ticks wraparound