diff options
author | Adam Jackson <ajax@redhat.com> | 2008-12-10 16:13:20 -0500 |
---|---|---|
committer | Adam Jackson <ajax@redhat.com> | 2008-12-11 10:31:48 -0500 |
commit | 1f4fb0225b278d1cf4145aebeb0bdd23dc8f62d5 (patch) | |
tree | 56ccd03a85d24c8d715f56c404dcbe464fff35f7 /Xext/sync.c | |
parent | 1a99110f0c221b79045ea26d61c4a1ec1e0d7341 (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
Diffstat (limited to 'Xext/sync.c')
-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 88fc03d03..d9e66b3f4 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 |