WaitFor.c revision f7df2e56
1/***********************************************************
2
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
26
27                        All Rights Reserved
28
29Permission to use, copy, modify, and distribute this software and its
30documentation for any purpose and without fee is hereby granted,
31provided that the above copyright notice appear in all copies and that
32both that copyright notice and this permission notice appear in
33supporting documentation, and that the name of Digital not be
34used in advertising or publicity pertaining to distribution of the
35software without specific, written prior permission.
36
37DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43SOFTWARE.
44
45******************************************************************/
46
47/*****************************************************************
48 * OS Dependent input routines:
49 *
50 *  WaitForSomething
51 *  TimerForce, TimerSet, TimerCheck, TimerFree
52 *
53 *****************************************************************/
54
55#include <X11/Xpoll.h>
56
57#ifdef HAVE_DIX_CONFIG_H
58#include <dix-config.h>
59#endif
60
61#ifdef WIN32
62#include <X11/Xwinsock.h>
63#endif
64#include <X11/Xos.h>            /* for strings, fcntl, time */
65#include <errno.h>
66#include <stdio.h>
67#include <X11/X.h>
68#include "misc.h"
69
70#include "osdep.h"
71#include "dixstruct.h"
72#include "opaque.h"
73#ifdef DPMSExtension
74#include "dpmsproc.h"
75#endif
76#include "busfault.h"
77
78#ifdef WIN32
79/* Error codes from windows sockets differ from fileio error codes  */
80#undef EINTR
81#define EINTR WSAEINTR
82#undef EINVAL
83#define EINVAL WSAEINVAL
84#undef EBADF
85#define EBADF WSAENOTSOCK
86/* Windows select does not set errno. Use GetErrno as wrapper for
87   WSAGetLastError */
88#define GetErrno WSAGetLastError
89#else
90/* This is just a fallback to errno to hide the differences between unix and
91   Windows in the code */
92#define GetErrno() errno
93#endif
94
95/* like ffs, but uses fd_mask instead of int as argument, so it works
96   when fd_mask is longer than an int, such as common 64-bit platforms */
97/* modifications by raphael */
98int
99mffs(fd_mask mask)
100{
101    int i;
102
103    if (!mask)
104        return 0;
105    i = 1;
106    while (!(mask & 1)) {
107        i++;
108        mask >>= 1;
109    }
110    return i;
111}
112
113#ifdef DPMSExtension
114#include <X11/extensions/dpmsconst.h>
115#endif
116
117struct _OsTimerRec {
118    OsTimerPtr next;
119    CARD32 expires;
120    CARD32 delta;
121    OsTimerCallback callback;
122    void *arg;
123};
124
125static void DoTimer(OsTimerPtr timer, CARD32 now, volatile OsTimerPtr *prev);
126static void CheckAllTimers(void);
127static volatile OsTimerPtr timers = NULL;
128
129/*****************
130 * WaitForSomething:
131 *     Make the server suspend until there is
132 *	1. data from clients or
133 *	2. input events available or
134 *	3. ddx notices something of interest (graphics
135 *	   queue ready, etc.) or
136 *	4. clients that have buffered replies/events are ready
137 *
138 *     If the time between INPUT events is
139 *     greater than ScreenSaverTime, the display is turned off (or
140 *     saved, depending on the hardware).  So, WaitForSomething()
141 *     has to handle this also (that's why the select() has a timeout.
142 *     For more info on ClientsWithInput, see ReadRequestFromClient().
143 *     pClientsReady is an array to store ready client->index values into.
144 *****************/
145
146int
147WaitForSomething(int *pClientsReady)
148{
149    int i;
150    struct timeval waittime, *wt;
151    INT32 timeout = 0;
152    fd_set clientsReadable;
153    fd_set clientsWritable;
154    int curclient;
155    int selecterr;
156    static int nready;
157    fd_set devicesReadable;
158    CARD32 now = 0;
159    Bool someReady = FALSE;
160
161    FD_ZERO(&clientsReadable);
162    FD_ZERO(&clientsWritable);
163
164    if (nready)
165        SmartScheduleStopTimer();
166    nready = 0;
167
168#ifdef BUSFAULT
169    busfault_check();
170#endif
171
172    /* We need a while loop here to handle
173       crashed connections and the screen saver timeout */
174    while (1) {
175        /* deal with any blocked jobs */
176        if (workQueue)
177            ProcessWorkQueue();
178        if (XFD_ANYSET(&ClientsWithInput)) {
179            if (!SmartScheduleDisable) {
180                someReady = TRUE;
181                waittime.tv_sec = 0;
182                waittime.tv_usec = 0;
183                wt = &waittime;
184            }
185            else {
186                XFD_COPYSET(&ClientsWithInput, &clientsReadable);
187                break;
188            }
189        }
190        if (someReady) {
191            XFD_COPYSET(&AllSockets, &LastSelectMask);
192            XFD_UNSET(&LastSelectMask, &ClientsWithInput);
193        }
194        else {
195            wt = NULL;
196            if (timers) {
197                now = GetTimeInMillis();
198                timeout = timers->expires - now;
199                if (timeout > 0 && timeout > timers->delta + 250) {
200                    /* time has rewound.  reset the timers. */
201                    CheckAllTimers();
202                }
203
204                if (timers) {
205                    timeout = timers->expires - now;
206                    if (timeout < 0)
207                        timeout = 0;
208                    waittime.tv_sec = timeout / MILLI_PER_SECOND;
209                    waittime.tv_usec = (timeout % MILLI_PER_SECOND) *
210                        (1000000 / MILLI_PER_SECOND);
211                    wt = &waittime;
212                }
213            }
214            XFD_COPYSET(&AllSockets, &LastSelectMask);
215        }
216
217        BlockHandler((void *) &wt, (void *) &LastSelectMask);
218        if (NewOutputPending)
219            FlushAllOutput();
220        /* keep this check close to select() call to minimize race */
221        if (dispatchException)
222            i = -1;
223        else if (AnyClientsWriteBlocked) {
224            XFD_COPYSET(&ClientsWriteBlocked, &clientsWritable);
225            i = Select(MaxClients, &LastSelectMask, &clientsWritable, NULL, wt);
226        }
227        else {
228            i = Select(MaxClients, &LastSelectMask, NULL, NULL, wt);
229        }
230        selecterr = GetErrno();
231        WakeupHandler(i, (void *) &LastSelectMask);
232        if (i <= 0) {           /* An error or timeout occurred */
233            if (dispatchException)
234                return 0;
235            if (i < 0) {
236                if (selecterr == EBADF) {       /* Some client disconnected */
237                    CheckConnections();
238                    if (!XFD_ANYSET(&AllClients))
239                        return 0;
240                }
241                else if (selecterr == EINVAL) {
242                    FatalError("WaitForSomething(): select: %s\n",
243                               strerror(selecterr));
244                }
245                else if (selecterr != EINTR && selecterr != EAGAIN) {
246                    ErrorF("WaitForSomething(): select: %s\n",
247                           strerror(selecterr));
248                }
249            }
250            else if (someReady) {
251                /*
252                 * If no-one else is home, bail quickly
253                 */
254                XFD_COPYSET(&ClientsWithInput, &LastSelectMask);
255                XFD_COPYSET(&ClientsWithInput, &clientsReadable);
256                break;
257            }
258            if (*checkForInput[0] != *checkForInput[1])
259                return 0;
260
261            if (timers) {
262                int expired = 0;
263
264                now = GetTimeInMillis();
265                if ((int) (timers->expires - now) <= 0)
266                    expired = 1;
267
268                if (expired) {
269                    OsBlockSignals();
270                    while (timers && (int) (timers->expires - now) <= 0)
271                        DoTimer(timers, now, &timers);
272                    OsReleaseSignals();
273
274                    return 0;
275                }
276            }
277        }
278        else {
279            fd_set tmp_set;
280
281            if (*checkForInput[0] == *checkForInput[1]) {
282                if (timers) {
283                    int expired = 0;
284
285                    now = GetTimeInMillis();
286                    if ((int) (timers->expires - now) <= 0)
287                        expired = 1;
288
289                    if (expired) {
290                        OsBlockSignals();
291                        while (timers && (int) (timers->expires - now) <= 0)
292                            DoTimer(timers, now, &timers);
293                        OsReleaseSignals();
294
295                        return 0;
296                    }
297                }
298            }
299            if (someReady)
300                XFD_ORSET(&LastSelectMask, &ClientsWithInput, &LastSelectMask);
301            if (AnyClientsWriteBlocked && XFD_ANYSET(&clientsWritable)) {
302                NewOutputPending = TRUE;
303                XFD_ORSET(&OutputPending, &clientsWritable, &OutputPending);
304                XFD_UNSET(&ClientsWriteBlocked, &clientsWritable);
305                if (!XFD_ANYSET(&ClientsWriteBlocked))
306                    AnyClientsWriteBlocked = FALSE;
307            }
308
309            XFD_ANDSET(&devicesReadable, &LastSelectMask, &EnabledDevices);
310            XFD_ANDSET(&clientsReadable, &LastSelectMask, &AllClients);
311            XFD_ANDSET(&tmp_set, &LastSelectMask, &WellKnownConnections);
312            if (XFD_ANYSET(&tmp_set))
313                QueueWorkProc(EstablishNewConnections, NULL,
314                              (void *) &LastSelectMask);
315
316            if (XFD_ANYSET(&devicesReadable) || XFD_ANYSET(&clientsReadable))
317                break;
318            /* check here for DDXes that queue events during Block/Wakeup */
319            if (*checkForInput[0] != *checkForInput[1])
320                return 0;
321        }
322    }
323
324    nready = 0;
325    if (XFD_ANYSET(&clientsReadable)) {
326#ifndef WIN32
327        for (i = 0; i < howmany(XFD_SETSIZE, NFDBITS); i++) {
328            int highest_priority = 0;
329
330            while (clientsReadable.fds_bits[i]) {
331                int client_priority, client_index;
332
333                curclient = mffs(clientsReadable.fds_bits[i]) - 1;
334                client_index =  /* raphael: modified */
335                    ConnectionTranslation[curclient +
336                                          (i * (sizeof(fd_mask) * 8))];
337#else
338        int highest_priority = 0;
339        fd_set savedClientsReadable;
340
341        XFD_COPYSET(&clientsReadable, &savedClientsReadable);
342        for (i = 0; i < XFD_SETCOUNT(&savedClientsReadable); i++) {
343            int client_priority, client_index;
344
345            curclient = XFD_FD(&savedClientsReadable, i);
346            client_index = GetConnectionTranslation(curclient);
347#endif
348            /*  We implement "strict" priorities.
349             *  Only the highest priority client is returned to
350             *  dix.  If multiple clients at the same priority are
351             *  ready, they are all returned.  This means that an
352             *  aggressive client could take over the server.
353             *  This was not considered a big problem because
354             *  aggressive clients can hose the server in so many
355             *  other ways :)
356             */
357            client_priority = clients[client_index]->priority;
358            if (nready == 0 || client_priority > highest_priority) {
359                /*  Either we found the first client, or we found
360                 *  a client whose priority is greater than all others
361                 *  that have been found so far.  Either way, we want
362                 *  to initialize the list of clients to contain just
363                 *  this client.
364                 */
365                pClientsReady[0] = client_index;
366                highest_priority = client_priority;
367                nready = 1;
368            }
369            /*  the following if makes sure that multiple same-priority
370             *  clients get batched together
371             */
372            else if (client_priority == highest_priority) {
373                pClientsReady[nready++] = client_index;
374            }
375#ifndef WIN32
376            clientsReadable.fds_bits[i] &= ~(((fd_mask) 1L) << curclient);
377        }
378#else
379            FD_CLR(curclient, &clientsReadable);
380#endif
381        }
382    }
383
384    if (nready)
385        SmartScheduleStartTimer();
386
387    return nready;
388}
389
390/* If time has rewound, re-run every affected timer.
391 * Timers might drop out of the list, so we have to restart every time. */
392static void
393CheckAllTimers(void)
394{
395    OsTimerPtr timer;
396    CARD32 now;
397
398    OsBlockSignals();
399 start:
400    now = GetTimeInMillis();
401
402    for (timer = timers; timer; timer = timer->next) {
403        if (timer->expires - now > timer->delta + 250) {
404            TimerForce(timer);
405            goto start;
406        }
407    }
408    OsReleaseSignals();
409}
410
411static void
412DoTimer(OsTimerPtr timer, CARD32 now, volatile OsTimerPtr *prev)
413{
414    CARD32 newTime;
415
416    OsBlockSignals();
417    *prev = timer->next;
418    timer->next = NULL;
419    OsReleaseSignals();
420
421    newTime = (*timer->callback) (timer, now, timer->arg);
422    if (newTime)
423        TimerSet(timer, 0, newTime, timer->callback, timer->arg);
424}
425
426OsTimerPtr
427TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
428         OsTimerCallback func, void *arg)
429{
430    volatile OsTimerPtr *prev;
431    CARD32 now = GetTimeInMillis();
432
433    if (!timer) {
434        timer = malloc(sizeof(struct _OsTimerRec));
435        if (!timer)
436            return NULL;
437    }
438    else {
439        OsBlockSignals();
440        for (prev = &timers; *prev; prev = &(*prev)->next) {
441            if (*prev == timer) {
442                *prev = timer->next;
443                if (flags & TimerForceOld)
444                    (void) (*timer->callback) (timer, now, timer->arg);
445                break;
446            }
447        }
448        OsReleaseSignals();
449    }
450    if (!millis)
451        return timer;
452    if (flags & TimerAbsolute) {
453        timer->delta = millis - now;
454    }
455    else {
456        timer->delta = millis;
457        millis += now;
458    }
459    timer->expires = millis;
460    timer->callback = func;
461    timer->arg = arg;
462    if ((int) (millis - now) <= 0) {
463        timer->next = NULL;
464        millis = (*timer->callback) (timer, now, timer->arg);
465        if (!millis)
466            return timer;
467    }
468    OsBlockSignals();
469    for (prev = &timers;
470         *prev && (int) ((*prev)->expires - millis) <= 0;
471         prev = &(*prev)->next);
472    timer->next = *prev;
473    *prev = timer;
474    OsReleaseSignals();
475    return timer;
476}
477
478Bool
479TimerForce(OsTimerPtr timer)
480{
481    int rc = FALSE;
482    volatile OsTimerPtr *prev;
483
484    OsBlockSignals();
485    for (prev = &timers; *prev; prev = &(*prev)->next) {
486        if (*prev == timer) {
487            DoTimer(timer, GetTimeInMillis(), prev);
488            rc = TRUE;
489            break;
490        }
491    }
492    OsReleaseSignals();
493    return rc;
494}
495
496void
497TimerCancel(OsTimerPtr timer)
498{
499    volatile OsTimerPtr *prev;
500
501    if (!timer)
502        return;
503    OsBlockSignals();
504    for (prev = &timers; *prev; prev = &(*prev)->next) {
505        if (*prev == timer) {
506            *prev = timer->next;
507            break;
508        }
509    }
510    OsReleaseSignals();
511}
512
513void
514TimerFree(OsTimerPtr timer)
515{
516    if (!timer)
517        return;
518    TimerCancel(timer);
519    free(timer);
520}
521
522void
523TimerCheck(void)
524{
525    CARD32 now = GetTimeInMillis();
526
527    if (timers && (int) (timers->expires - now) <= 0) {
528        OsBlockSignals();
529        while (timers && (int) (timers->expires - now) <= 0)
530            DoTimer(timers, now, &timers);
531        OsReleaseSignals();
532    }
533}
534
535void
536TimerInit(void)
537{
538    OsTimerPtr timer;
539
540    while ((timer = timers)) {
541        timers = timer->next;
542        free(timer);
543    }
544}
545
546#ifdef DPMSExtension
547
548#define DPMS_CHECK_MODE(mode,time)\
549    if (time > 0 && DPMSPowerLevel < mode && timeout >= time)\
550	DPMSSet(serverClient, mode);
551
552#define DPMS_CHECK_TIMEOUT(time)\
553    if (time > 0 && (time - timeout) > 0)\
554	return time - timeout;
555
556static CARD32
557NextDPMSTimeout(INT32 timeout)
558{
559    /*
560     * Return the amount of time remaining until we should set
561     * the next power level. Fallthroughs are intentional.
562     */
563    switch (DPMSPowerLevel) {
564    case DPMSModeOn:
565        DPMS_CHECK_TIMEOUT(DPMSStandbyTime)
566
567    case DPMSModeStandby:
568        DPMS_CHECK_TIMEOUT(DPMSSuspendTime)
569
570    case DPMSModeSuspend:
571        DPMS_CHECK_TIMEOUT(DPMSOffTime)
572
573    default:                   /* DPMSModeOff */
574        return 0;
575    }
576}
577#endif                          /* DPMSExtension */
578
579static CARD32
580ScreenSaverTimeoutExpire(OsTimerPtr timer, CARD32 now, void *arg)
581{
582    INT32 timeout = now - LastEventTime(XIAllDevices).milliseconds;
583    CARD32 nextTimeout = 0;
584
585#ifdef DPMSExtension
586    /*
587     * Check each mode lowest to highest, since a lower mode can
588     * have the same timeout as a higher one.
589     */
590    if (DPMSEnabled) {
591        DPMS_CHECK_MODE(DPMSModeOff, DPMSOffTime)
592            DPMS_CHECK_MODE(DPMSModeSuspend, DPMSSuspendTime)
593            DPMS_CHECK_MODE(DPMSModeStandby, DPMSStandbyTime)
594
595            nextTimeout = NextDPMSTimeout(timeout);
596    }
597
598    /*
599     * Only do the screensaver checks if we're not in a DPMS
600     * power saving mode
601     */
602    if (DPMSPowerLevel != DPMSModeOn)
603        return nextTimeout;
604#endif                          /* DPMSExtension */
605
606    if (!ScreenSaverTime)
607        return nextTimeout;
608
609    if (timeout < ScreenSaverTime) {
610        return nextTimeout > 0 ?
611            min(ScreenSaverTime - timeout, nextTimeout) :
612            ScreenSaverTime - timeout;
613    }
614
615    ResetOsBuffers();           /* not ideal, but better than nothing */
616    dixSaveScreens(serverClient, SCREEN_SAVER_ON, ScreenSaverActive);
617
618    if (ScreenSaverInterval > 0) {
619        nextTimeout = nextTimeout > 0 ?
620            min(ScreenSaverInterval, nextTimeout) : ScreenSaverInterval;
621    }
622
623    return nextTimeout;
624}
625
626static OsTimerPtr ScreenSaverTimer = NULL;
627
628void
629FreeScreenSaverTimer(void)
630{
631    if (ScreenSaverTimer) {
632        TimerFree(ScreenSaverTimer);
633        ScreenSaverTimer = NULL;
634    }
635}
636
637void
638SetScreenSaverTimer(void)
639{
640    CARD32 timeout = 0;
641
642#ifdef DPMSExtension
643    if (DPMSEnabled) {
644        /*
645         * A higher DPMS level has a timeout that's either less
646         * than or equal to that of a lower DPMS level.
647         */
648        if (DPMSStandbyTime > 0)
649            timeout = DPMSStandbyTime;
650
651        else if (DPMSSuspendTime > 0)
652            timeout = DPMSSuspendTime;
653
654        else if (DPMSOffTime > 0)
655            timeout = DPMSOffTime;
656    }
657#endif
658
659    if (ScreenSaverTime > 0) {
660        timeout = timeout > 0 ? min(ScreenSaverTime, timeout) : ScreenSaverTime;
661    }
662
663#ifdef SCREENSAVER
664    if (timeout && !screenSaverSuspended) {
665#else
666    if (timeout) {
667#endif
668        ScreenSaverTimer = TimerSet(ScreenSaverTimer, 0, timeout,
669                                    ScreenSaverTimeoutExpire, NULL);
670    }
671    else if (ScreenSaverTimer) {
672        FreeScreenSaverTimer();
673    }
674}
675