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