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:
parent
efc1035ca9
commit
06b87aa528
32
Xext/sync.c
32
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,12 +2708,26 @@ 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
|
||||
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(FALSE);
|
||||
RegisterBlockAndWakeupHandlers(IdleTimeBlockHandler,
|
||||
IdleTimeWakeupHandler, pCounter);
|
||||
}
|
||||
|
|
38
dix/events.c
38
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;
|
||||
|
|
|
@ -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 */ ,
|
||||
|
|
Loading…
Reference in New Issue
Block a user