[PATCH 5/7] sync: if the idle time was reset, force alarms to trigger (#70476)

Peter Hutterer peter.hutterer at who-t.net
Wed Oct 16 20:58:18 PDT 2013


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 at who-t.net>
---
 Xext/sync.c   | 50 ++++++++++++++++++++++++++++++++++++++------------
 dix/events.c  | 38 ++++++++++++++++++++++++++++++++++----
 include/dix.h |  6 ++++++
 3 files changed, 78 insertions(+), 16 deletions(-)

diff --git a/Xext/sync.c b/Xext/sync.c
index ed891b1..1bb7733 100644
--- a/Xext/sync.c
+++ b/Xext/sync.c
@@ -2686,19 +2686,8 @@ IdleTimeBlockHandler(pointer pCounter, struct timeval **wt, pointer LastSelectMa
 }
 
 static void
-IdleTimeWakeupHandler(pointer pCounter, int rc, pointer LastSelectMask)
+IdleTimeCheckBrackets(SyncCounter *counter, XSyncValue idle, XSyncValue *less, XSyncValue *greater)
 {
-    SyncCounter *counter = pCounter;
-    IdleCounterPriv *priv = SysCounterGetPrivate(counter);
-    XSyncValue *less = priv->value_less,
-               *greater = priv->value_greater;
-    XSyncValue idle;
-
-    if (!less && !greater)
-        return;
-
-    IdleTimeQueryValue(pCounter, &idle);
-
     if ((greater && XSyncValueGreaterOrEqual(idle, *greater)) ||
         (less && XSyncValueLessOrEqual(idle, *less))) {
         SyncChangeCounter(counter, idle);
@@ -2706,6 +2695,40 @@ IdleTimeWakeupHandler(pointer pCounter, int rc, pointer LastSelectMask)
 }
 
 static void
+IdleTimeWakeupHandler(pointer pCounter, int rc, pointer LastSelectMask)
+{
+    SyncCounter *counter = pCounter;
+    IdleCounterPriv *priv = SysCounterGetPrivate(counter);
+    XSyncValue *less = priv->value_less,
+               *greater = priv->value_greater;
+    XSyncValue idle;
+
+    if (!less && !greater)
+        return;
+
+    IdleTimeQueryValue(pCounter, &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, 0);
+        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
 IdleTimeBracketValues(pointer pCounter, CARD64 * pbracket_less,
                       CARD64 * pbracket_greater)
 {
@@ -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(0);
         RegisterBlockAndWakeupHandlers(IdleTimeBlockHandler,
                                        IdleTimeWakeupHandler, pCounter);
     }
diff --git a/dix/events.c b/dix/events.c
index c803721..ca0c085 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -262,7 +262,10 @@ InputInfo inputInfo;
 
 EventSyncInfoRec syncEvents;
 
-static TimeStamp lastDeviceEventTime[MAXDEVICES];
+static struct DeviceEventTime {
+    int 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, 1);
+    LastEventTimeToggleResetFlag(XIAllDevices, 1);
 }
 
 static void
@@ -1085,7 +1091,30 @@ NoticeEventTime(InternalEvent *ev, DeviceIntPtr dev)
 TimeStamp
 LastEventTime(int deviceid)
 {
-    return lastDeviceEventTime[deviceid];
+    return lastDeviceEventTime[deviceid].time;
+}
+
+int
+LastEventTimeWasReset(int deviceid)
+{
+    return lastDeviceEventTime[deviceid].reset;
+}
+
+void
+LastEventTimeToggleResetFlag(int deviceid, int state)
+{
+    lastDeviceEventTime[deviceid].reset = state;
+}
+
+void
+LastEventTimeToggleResetAll(int state)
+{
+    DeviceIntPtr dev;
+    nt_list_for_each_entry(dev, inputInfo.devices, next) {
+        LastEventTimeToggleResetFlag(dev->id, 0);
+    }
+    LastEventTimeToggleResetFlag(XIAllDevices, 0);
+    LastEventTimeToggleResetFlag(XIAllMasterDevices, 0);
 }
 
 /**************************************************************************
@@ -5297,6 +5326,7 @@ InitEvents(void)
 
         dummy.id = i;
         NoticeTime(&dummy, currentTime);
+        LastEventTimeToggleResetFlag(i, 0);
     }
 
     syncEvents.replayDev = (DeviceIntPtr) NULL;
diff --git a/include/dix.h b/include/dix.h
index fd2490f..f0c0d14 100644
--- a/include/dix.h
+++ b/include/dix.h
@@ -322,6 +322,12 @@ NoticeEventTime(InternalEvent *ev,
                 DeviceIntPtr dev);
 extern _X_EXPORT TimeStamp
 LastEventTime(int deviceid);
+extern _X_EXPORT int
+LastEventTimeWasReset(int deviceid);
+extern _X_EXPORT void
+LastEventTimeToggleResetFlag(int deviceid, int state);
+extern _X_EXPORT void
+LastEventTimeToggleResetAll(int state);
 
 extern void
 EnqueueEvent(InternalEvent * /* ev */ ,
-- 
1.8.3.1



More information about the xorg-devel mailing list