diff options
author | Adam Jackson <ajax@redhat.com> | 2008-12-10 16:13:20 -0500 |
---|---|---|
committer | Adam Jackson <ajax@redhat.com> | 2008-12-16 09:55:27 -0500 |
commit | ca56d764d2be28c64fe15c9e37d534ef00117ad2 (patch) | |
tree | 745d9bdc25c821beeaa35bb093e3c1eb4a64b4d8 | |
parent | a82f10c5dd9fa74ff18759ab288bbd9c8b7ac4de (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
(cherry picked from commit 1f4fb0225b278d1cf4145aebeb0bdd23dc8f62d5)
-rw-r--r-- | Xext/sync.c | 51 |
1 files changed, 41 insertions, 10 deletions
diff --git a/Xext/sync.c b/Xext/sync.c index 63f6fa2aa..1b3736644 100644 --- a/Xext/sync.c +++ b/Xext/sync.c @@ -2533,7 +2533,7 @@ SyncInitServerTime(void) * IDLETIME implementation */ -static pointer IdleTimeCounter; +static SyncCounter *IdleTimeCounter; static XSyncValue *pIdleTimeValueLess; static XSyncValue *pIdleTimeValueGreater; @@ -2545,38 +2545,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 |