From 06b87aa528d7a739ba20101a1f83b1a428691a01 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 16 Oct 2013 10:08:46 +1000 Subject: [PATCH] sync: if the idle time was reset, force alarms to trigger (#70476) 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 Signed-off-by: Peter Hutterer Reviewed-by: Adam Jackson Reviewed-by: Keith Packard --- Xext/sync.c | 32 +++++++++++++++++++++++++++++--- dix/events.c | 38 ++++++++++++++++++++++++++++++++++---- include/dix.h | 6 ++++++ 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Xext/sync.c b/Xext/sync.c index ed891b169..ab46d8972 100644 --- a/Xext/sync.c +++ b/Xext/sync.c @@ -2685,6 +2685,15 @@ IdleTimeBlockHandler(pointer pCounter, struct timeval **wt, pointer LastSelectMa counter->value = old_idle; /* pop */ } +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) { @@ -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); } diff --git a/dix/events.c b/dix/events.c index c803721f3..4632bb7db 100644 --- a/dix/events.c +++ b/dix/events.c @@ -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; diff --git a/include/dix.h b/include/dix.h index fd2490fbd..fa7ccd4a3 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 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 */ ,