sync: if the idle time was reset, force alarms to trigger (#70476)
authorPeter Hutterer <peter.hutterer@who-t.net>
Wed, 16 Oct 2013 00:08:46 +0000 (10:08 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Fri, 18 Oct 2013 06:44:32 +0000 (16:44 +1000)
The time between the idle reset and the IdleTimeWakeupHandler to be called is
indeterminate. Clients with an PositiveTransition or NegativeTransition alarm
on a low threshold may miss an alarm.

Work around this by keeping a reset flag for each device. When the
WakeupHandler triggers and the reset flag is set, we force a re-calculation of
everything and pretend the current idle time is zero. Immediately after is the
next calculation with the real idle time.

Relatively reproducible test case: Set up a XSyncNegativeTransition alarm for
a threshold of 1 ms. May trigger, may not.

X.Org Bug 70476 <http://bugs.freedesktop.org/show_bug.cgi?id=70476>

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Adam Jackson <ajax@redhat.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Xext/sync.c
dix/events.c
include/dix.h

index ed891b1..ab46d89 100644 (file)
@@ -2686,6 +2686,15 @@ IdleTimeBlockHandler(pointer pCounter, struct timeval **wt, pointer LastSelectMa
 }
 
 static void
+IdleTimeCheckBrackets(SyncCounter *counter, XSyncValue idle, XSyncValue *less, XSyncValue *greater)
+{
+    if ((greater && XSyncValueGreaterOrEqual(idle, *greater)) ||
+        (less && XSyncValueLessOrEqual(idle, *less))) {
+        SyncChangeCounter(counter, idle);
+    }
+}
+
+static void
 IdleTimeWakeupHandler(pointer pCounter, int rc, pointer LastSelectMask)
 {
     SyncCounter *counter = pCounter;
@@ -2699,10 +2708,24 @@ IdleTimeWakeupHandler(pointer pCounter, int rc, pointer LastSelectMask)
 
     IdleTimeQueryValue(pCounter, &idle);
 
-    if ((greater && XSyncValueGreaterOrEqual(idle, *greater)) ||
-        (less && XSyncValueLessOrEqual(idle, *less))) {
-        SyncChangeCounter(counter, idle);
+    /*
+      There is no guarantee for the WakeupHandler to be called within a specific
+      timeframe. Idletime may go to 0, but by the time we get here, it may be
+      non-zero and alarms for a pos. transition on 0 won't get triggered.
+      https://bugs.freedesktop.org/show_bug.cgi?id=70476
+      */
+    if (LastEventTimeWasReset(priv->deviceid)) {
+        LastEventTimeToggleResetFlag(priv->deviceid, FALSE);
+        if (!XSyncValueIsZero(idle)) {
+            XSyncValue zero;
+            XSyncIntsToValue(&zero, 0, 0);
+            IdleTimeCheckBrackets(counter, zero, less, greater);
+            less = priv->value_less;
+            greater = priv->value_greater;
+        }
     }
+
+    IdleTimeCheckBrackets(counter, idle, less, greater);
 }
 
 static void
@@ -2720,6 +2743,9 @@ IdleTimeBracketValues(pointer pCounter, CARD64 * pbracket_less,
                                      IdleTimeWakeupHandler, pCounter);
     }
     else if (!registered && (pbracket_less || pbracket_greater)) {
+        /* Reset flag must be zero so we don't force a idle timer reset on
+           the first wakeup */
+        LastEventTimeToggleResetAll(FALSE);
         RegisterBlockAndWakeupHandlers(IdleTimeBlockHandler,
                                        IdleTimeWakeupHandler, pCounter);
     }
index c803721..4632bb7 100644 (file)
@@ -262,7 +262,10 @@ InputInfo inputInfo;
 
 EventSyncInfoRec syncEvents;
 
-static TimeStamp lastDeviceEventTime[MAXDEVICES];
+static struct DeviceEventTime {
+    Bool reset;
+    TimeStamp time;
+} lastDeviceEventTime[MAXDEVICES];
 
 /**
  * The root window the given device is currently on.
@@ -1060,8 +1063,11 @@ MonthChangedOrBadTime(CARD32 *ms)
 void
 NoticeTime(const DeviceIntPtr dev, TimeStamp time)
 {
-    lastDeviceEventTime[XIAllDevices] = currentTime;
-    lastDeviceEventTime[dev->id] = currentTime;
+    lastDeviceEventTime[XIAllDevices].time = currentTime;
+    lastDeviceEventTime[dev->id].time = currentTime;
+
+    LastEventTimeToggleResetFlag(dev->id, TRUE);
+    LastEventTimeToggleResetFlag(XIAllDevices, TRUE);
 }
 
 static void
@@ -1085,7 +1091,30 @@ NoticeEventTime(InternalEvent *ev, DeviceIntPtr dev)
 TimeStamp
 LastEventTime(int deviceid)
 {
-    return lastDeviceEventTime[deviceid];
+    return lastDeviceEventTime[deviceid].time;
+}
+
+Bool
+LastEventTimeWasReset(int deviceid)
+{
+    return lastDeviceEventTime[deviceid].reset;
+}
+
+void
+LastEventTimeToggleResetFlag(int deviceid, Bool state)
+{
+    lastDeviceEventTime[deviceid].reset = state;
+}
+
+void
+LastEventTimeToggleResetAll(Bool state)
+{
+    DeviceIntPtr dev;
+    nt_list_for_each_entry(dev, inputInfo.devices, next) {
+        LastEventTimeToggleResetFlag(dev->id, FALSE);
+    }
+    LastEventTimeToggleResetFlag(XIAllDevices, FALSE);
+    LastEventTimeToggleResetFlag(XIAllMasterDevices, FALSE);
 }
 
 /**************************************************************************
@@ -5297,6 +5326,7 @@ InitEvents(void)
 
         dummy.id = i;
         NoticeTime(&dummy, currentTime);
+        LastEventTimeToggleResetFlag(i, FALSE);
     }
 
     syncEvents.replayDev = (DeviceIntPtr) NULL;
index fd2490f..fa7ccd4 100644 (file)
@@ -322,6 +322,12 @@ NoticeEventTime(InternalEvent *ev,
                 DeviceIntPtr dev);
 extern _X_EXPORT TimeStamp
 LastEventTime(int deviceid);
+extern _X_EXPORT Bool
+LastEventTimeWasReset(int deviceid);
+extern _X_EXPORT void
+LastEventTimeToggleResetFlag(int deviceid, Bool state);
+extern _X_EXPORT void
+LastEventTimeToggleResetAll(Bool state);
 
 extern void
 EnqueueEvent(InternalEvent * /* ev */ ,