summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jackson <ajax@redhat.com>2008-12-10 21:13:20 (GMT)
committerAdam Jackson <ajax@redhat.com>2008-12-11 15:31:48 (GMT)
commit1f4fb0225b278d1cf4145aebeb0bdd23dc8f62d5 (patch)
tree56ccd03a85d24c8d715f56c404dcbe464fff35f7
parent1a99110f0c221b79045ea26d61c4a1ec1e0d7341 (diff)
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
-rw-r--r--Xext/sync.c51
1 files changed, 41 insertions, 10 deletions
diff --git a/Xext/sync.c b/Xext/sync.c
index 88fc03d..d9e66b3 100644
--- a/Xext/sync.c
+++ b/Xext/sync.c
@@ -2281,7 +2281,7 @@ SyncInitServerTime(void)
* IDLETIME implementation
*/
-static pointer IdleTimeCounter;
+static SyncCounter *IdleTimeCounter;
static XSyncValue *pIdleTimeValueLess;
static XSyncValue *pIdleTimeValueGreater;
@@ -2293,38 +2293,69 @@ IdleTimeQueryValue (pointer pCounter, CARD64 *pValue_return)
}
static void
-IdleTimeBlockHandler (pointer env,
- struct timeval **wt,
- pointer LastSelectMask)
+IdleTimeBlockHandler(pointer env, struct timeval **wt, pointer LastSelectMask)
{
- XSyncValue idle;
+ XSyncValue idle, old_idle;
+ SyncTriggerList *list = IdleTimeCounter->pTriglist;
+ SyncTrigger *trig;
if (!pIdleTimeValueLess && !pIdleTimeValueGreater)
return;
+ old_idle = IdleTimeCounter->value;
IdleTimeQueryValue (NULL, &idle);
+ IdleTimeCounter->value = idle; /* push, so CheckTrigger works */
if (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)
{
- 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;
Bool overflow;
XSyncValueSubtract (&value, *pIdleTimeValueGreater,
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);
}
+
+ IdleTimeCounter->value = old_idle; /* pop */
}
static void