xsync: Fix wakeup storm in idletime counter.

Wakeup scheduling only considered the threshold values, and not whether
the trigger was edge or level.

See also:
https://bugzilla.redhat.com/show_bug.cgi?id=474586
http://svn.gnome.org/viewvc/gnome-screensaver/trunk/src/test-idle-ext.c?view=markup
This commit is contained in:
Adam Jackson 2008-12-10 16:13:20 -05:00
parent 1a99110f0c
commit 1f4fb0225b

View File

@ -2281,7 +2281,7 @@ SyncInitServerTime(void)
* IDLETIME implementation * IDLETIME implementation
*/ */
static pointer IdleTimeCounter; static SyncCounter *IdleTimeCounter;
static XSyncValue *pIdleTimeValueLess; static XSyncValue *pIdleTimeValueLess;
static XSyncValue *pIdleTimeValueGreater; static XSyncValue *pIdleTimeValueGreater;
@ -2293,38 +2293,69 @@ IdleTimeQueryValue (pointer pCounter, CARD64 *pValue_return)
} }
static void static void
IdleTimeBlockHandler (pointer env, IdleTimeBlockHandler(pointer env, struct timeval **wt, pointer LastSelectMask)
struct timeval **wt,
pointer LastSelectMask)
{ {
XSyncValue idle; XSyncValue idle, old_idle;
SyncTriggerList *list = IdleTimeCounter->pTriglist;
SyncTrigger *trig;
if (!pIdleTimeValueLess && !pIdleTimeValueGreater) if (!pIdleTimeValueLess && !pIdleTimeValueGreater)
return; return;
old_idle = IdleTimeCounter->value;
IdleTimeQueryValue (NULL, &idle); IdleTimeQueryValue (NULL, &idle);
IdleTimeCounter->value = idle; /* push, so CheckTrigger works */
if (pIdleTimeValueLess && if (pIdleTimeValueLess &&
XSyncValueLessOrEqual (idle, *pIdleTimeValueLess)) XSyncValueLessOrEqual (idle, *pIdleTimeValueLess))
{ {
AdjustWaitForDelay (wt, 0); /*
* We've been idle for less than the threshold value, and someone
* wants to know about that, but now we need to know whether they
* want level or edge trigger. Check the trigger list against the
* current idle time, and if any succeed, bomb out of select()
* immediately so we can reschedule.
*/
for (list = IdleTimeCounter->pTriglist; list; list = list->next) {
trig = list->pTrigger;
if (trig->CheckTrigger(trig, old_idle)) {
AdjustWaitForDelay(wt, 0);
break;
}
}
} }
else if (pIdleTimeValueGreater) else if (pIdleTimeValueGreater)
{ {
unsigned long timeout = 0; /*
* There's a threshold in the positive direction. If we've been
* idle less than it, schedule a wakeup for sometime in the future.
* If we've been idle more than it, and someone wants to know about
* that level-triggered, schedule an immediate wakeup.
*/
unsigned long timeout = -1;
if (XSyncValueLessThan (idle, *pIdleTimeValueGreater)) if (XSyncValueLessThan (idle, *pIdleTimeValueGreater)) {
{
XSyncValue value; XSyncValue value;
Bool overflow; Bool overflow;
XSyncValueSubtract (&value, *pIdleTimeValueGreater, XSyncValueSubtract (&value, *pIdleTimeValueGreater,
idle, &overflow); idle, &overflow);
timeout = XSyncValueLow32 (value); timeout = min(timeout, XSyncValueLow32 (value));
} else {
for (list = IdleTimeCounter->pTriglist; list; list = list->next) {
trig = list->pTrigger;
if (trig->CheckTrigger(trig, old_idle)) {
timeout = min(timeout, 0);
break;
}
}
} }
AdjustWaitForDelay (wt, timeout); AdjustWaitForDelay (wt, timeout);
} }
IdleTimeCounter->value = old_idle; /* pop */
} }
static void static void