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 <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>
This commit is contained in:
Peter Hutterer 2013-10-16 10:08:46 +10:00
parent efc1035ca9
commit 06b87aa528
3 changed files with 69 additions and 7 deletions

View File

@ -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);
}

View 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;

View 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 */ ,