1/* 2 * tclUnixNotify.c -- 3 * 4 * This file contains the implementation of the select()-based 5 * Unix-specific notifier, which is the lowest-level part of the Tcl 6 * event loop. This file works together with generic/tclNotify.c. 7 * 8 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id: tclUnixNotfy.c,v 1.34.2.2 2010/01/29 09:38:47 nijtmans Exp $ 14 */ 15 16#include "tclInt.h" 17#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier is 18 * in tclMacOSXNotify.c */ 19#include <signal.h> 20 21/* 22 * This code does deep stub magic to allow replacement of the notifier at 23 * runtime. 24 */ 25 26extern TclStubs tclStubs; 27extern Tcl_NotifierProcs tclOriginalNotifier; 28 29/* 30 * This structure is used to keep track of the notifier info for a registered 31 * file. 32 */ 33 34typedef struct FileHandler { 35 int fd; 36 int mask; /* Mask of desired events: TCL_READABLE, 37 * etc. */ 38 int readyMask; /* Mask of events that have been seen since 39 * the last time file handlers were invoked 40 * for this file. */ 41 Tcl_FileProc *proc; /* Function to call, in the style of 42 * Tcl_CreateFileHandler. */ 43 ClientData clientData; /* Argument to pass to proc. */ 44 struct FileHandler *nextPtr;/* Next in list of all files we care about. */ 45} FileHandler; 46 47/* 48 * The following structure is what is added to the Tcl event queue when file 49 * handlers are ready to fire. 50 */ 51 52typedef struct FileHandlerEvent { 53 Tcl_Event header; /* Information that is standard for all 54 * events. */ 55 int fd; /* File descriptor that is ready. Used to find 56 * the FileHandler structure for the file 57 * (can't point directly to the FileHandler 58 * structure because it could go away while 59 * the event is queued). */ 60} FileHandlerEvent; 61 62/* 63 * The following structure contains a set of select() masks to track readable, 64 * writable, and exceptional conditions. 65 */ 66 67typedef struct SelectMasks { 68 fd_set readable; 69 fd_set writable; 70 fd_set exceptional; 71} SelectMasks; 72 73/* 74 * The following static structure contains the state information for the 75 * select based implementation of the Tcl notifier. One of these structures is 76 * created for each thread that is using the notifier. 77 */ 78 79typedef struct ThreadSpecificData { 80 FileHandler *firstFileHandlerPtr; 81 /* Pointer to head of file handler list. */ 82 SelectMasks checkMasks; /* This structure is used to build up the 83 * masks to be used in the next call to 84 * select. Bits are set in response to calls 85 * to Tcl_CreateFileHandler. */ 86 SelectMasks readyMasks; /* This array reflects the readable/writable 87 * conditions that were found to exist by the 88 * last call to select. */ 89 int numFdBits; /* Number of valid bits in checkMasks (one 90 * more than highest fd for which 91 * Tcl_WatchFile has been called). */ 92#ifdef TCL_THREADS 93 int onList; /* True if it is in this list */ 94 unsigned int pollState; /* pollState is used to implement a polling 95 * handshake between each thread and the 96 * notifier thread. Bits defined below. */ 97 struct ThreadSpecificData *nextPtr, *prevPtr; 98 /* All threads that are currently waiting on 99 * an event have their ThreadSpecificData 100 * structure on a doubly-linked listed formed 101 * from these pointers. You must hold the 102 * notifierMutex lock before accessing these 103 * fields. */ 104 Tcl_Condition waitCV; /* Any other thread alerts a notifier that an 105 * event is ready to be processed by signaling 106 * this condition variable. */ 107 int eventReady; /* True if an event is ready to be processed. 108 * Used as condition flag together with waitCV 109 * above. */ 110#endif 111} ThreadSpecificData; 112 113static Tcl_ThreadDataKey dataKey; 114 115#ifdef TCL_THREADS 116/* 117 * The following static indicates the number of threads that have initialized 118 * notifiers. 119 * 120 * You must hold the notifierMutex lock before accessing this variable. 121 */ 122 123static int notifierCount = 0; 124 125/* 126 * The following variable points to the head of a doubly-linked list of 127 * ThreadSpecificData structures for all threads that are currently waiting on 128 * an event. 129 * 130 * You must hold the notifierMutex lock before accessing this list. 131 */ 132 133static ThreadSpecificData *waitingListPtr = NULL; 134 135/* 136 * The notifier thread spends all its time in select() waiting for a file 137 * descriptor associated with one of the threads on the waitingListPtr list to 138 * do something interesting. But if the contents of the waitingListPtr list 139 * ever changes, we need to wake up and restart the select() system call. You 140 * can wake up the notifier thread by writing a single byte to the file 141 * descriptor defined below. This file descriptor is the input-end of a pipe 142 * and the notifier thread is listening for data on the output-end of the same 143 * pipe. Hence writing to this file descriptor will cause the select() system 144 * call to return and wake up the notifier thread. 145 * 146 * You must hold the notifierMutex lock before writing to the pipe. 147 */ 148 149static int triggerPipe = -1; 150 151/* 152 * The notifierMutex locks access to all of the global notifier state. 153 */ 154 155TCL_DECLARE_MUTEX(notifierMutex) 156 157/* 158 * The notifier thread signals the notifierCV when it has finished 159 * initializing the triggerPipe and right before the notifier thread 160 * terminates. 161 */ 162 163static Tcl_Condition notifierCV; 164 165/* 166 * The pollState bits 167 * POLL_WANT is set by each thread before it waits on its condition 168 * variable. It is checked by the notifier before it does select. 169 * POLL_DONE is set by the notifier if it goes into select after seeing 170 * POLL_WANT. The idea is to ensure it tries a select with the 171 * same bits the initial thread had set. 172 */ 173 174#define POLL_WANT 0x1 175#define POLL_DONE 0x2 176 177/* 178 * This is the thread ID of the notifier thread that does select. 179 */ 180 181static Tcl_ThreadId notifierThread; 182 183#endif 184 185/* 186 * Static routines defined in this file. 187 */ 188 189#ifdef TCL_THREADS 190static void NotifierThreadProc(ClientData clientData); 191#endif 192static int FileHandlerEventProc(Tcl_Event *evPtr, int flags); 193 194/* 195 *---------------------------------------------------------------------- 196 * 197 * Tcl_InitNotifier -- 198 * 199 * Initializes the platform specific notifier state. 200 * 201 * Results: 202 * Returns a handle to the notifier state for this thread. 203 * 204 * Side effects: 205 * None. 206 * 207 *---------------------------------------------------------------------- 208 */ 209 210ClientData 211Tcl_InitNotifier(void) 212{ 213 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 214 215#ifdef TCL_THREADS 216 tsdPtr->eventReady = 0; 217 218 /* 219 * Start the Notifier thread if necessary. 220 */ 221 222 Tcl_MutexLock(¬ifierMutex); 223 if (notifierCount == 0) { 224 if (TclpThreadCreate(¬ifierThread, NotifierThreadProc, NULL, 225 TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) { 226 Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread"); 227 } 228 } 229 notifierCount++; 230 231 /* 232 * Wait for the notifier pipe to be created. 233 */ 234 235 while (triggerPipe < 0) { 236 Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); 237 } 238 239 Tcl_MutexUnlock(¬ifierMutex); 240#endif 241 return (ClientData) tsdPtr; 242} 243 244/* 245 *---------------------------------------------------------------------- 246 * 247 * Tcl_FinalizeNotifier -- 248 * 249 * This function is called to cleanup the notifier state before a thread 250 * is terminated. 251 * 252 * Results: 253 * None. 254 * 255 * Side effects: 256 * May terminate the background notifier thread if this is the last 257 * notifier instance. 258 * 259 *---------------------------------------------------------------------- 260 */ 261 262void 263Tcl_FinalizeNotifier( 264 ClientData clientData) /* Not used. */ 265{ 266#ifdef TCL_THREADS 267 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 268 269 Tcl_MutexLock(¬ifierMutex); 270 notifierCount--; 271 272 /* 273 * If this is the last thread to use the notifier, close the notifier pipe 274 * and wait for the background thread to terminate. 275 */ 276 277 if (notifierCount == 0) { 278 int result; 279 280 if (triggerPipe < 0) { 281 Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized"); 282 } 283 284 /* 285 * Send "q" message to the notifier thread so that it will terminate. 286 * The notifier will return from its call to select() and notice that 287 * a "q" message has arrived, it will then close its side of the pipe 288 * and terminate its thread. Note the we can not just close the pipe 289 * and check for EOF in the notifier thread because if a background 290 * child process was created with exec, select() would not register 291 * the EOF on the pipe until the child processes had terminated. [Bug: 292 * 4139] [Bug: 1222872] 293 */ 294 295 if (write(triggerPipe, "q", 1) != 1) { 296 Tcl_Panic("Tcl_FinalizeNotifier: unable to write q to triggerPipe"); 297 } 298 close(triggerPipe); 299 while(triggerPipe >= 0) { 300 Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); 301 } 302 303 result = Tcl_JoinThread(notifierThread, NULL); 304 if (result) { 305 Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); 306 } 307 } 308 309 /* 310 * Clean up any synchronization objects in the thread local storage. 311 */ 312 313 Tcl_ConditionFinalize(&(tsdPtr->waitCV)); 314 315 Tcl_MutexUnlock(¬ifierMutex); 316#endif 317} 318 319/* 320 *---------------------------------------------------------------------- 321 * 322 * Tcl_AlertNotifier -- 323 * 324 * Wake up the specified notifier from any thread. This routine is called 325 * by the platform independent notifier code whenever the Tcl_ThreadAlert 326 * routine is called. This routine is guaranteed not to be called on a 327 * given notifier after Tcl_FinalizeNotifier is called for that notifier. 328 * 329 * Results: 330 * None. 331 * 332 * Side effects: 333 * Signals the notifier condition variable for the specified notifier. 334 * 335 *---------------------------------------------------------------------- 336 */ 337 338void 339Tcl_AlertNotifier( 340 ClientData clientData) 341{ 342#ifdef TCL_THREADS 343 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; 344 Tcl_MutexLock(¬ifierMutex); 345 tsdPtr->eventReady = 1; 346 Tcl_ConditionNotify(&tsdPtr->waitCV); 347 Tcl_MutexUnlock(¬ifierMutex); 348#endif 349} 350 351/* 352 *---------------------------------------------------------------------- 353 * 354 * Tcl_SetTimer -- 355 * 356 * This function sets the current notifier timer value. This interface is 357 * not implemented in this notifier because we are always running inside 358 * of Tcl_DoOneEvent. 359 * 360 * Results: 361 * None. 362 * 363 * Side effects: 364 * None. 365 * 366 *---------------------------------------------------------------------- 367 */ 368 369void 370Tcl_SetTimer( 371 Tcl_Time *timePtr) /* Timeout value, may be NULL. */ 372{ 373 /* 374 * The interval timer doesn't do anything in this implementation, because 375 * the only event loop is via Tcl_DoOneEvent, which passes timeout values 376 * to Tcl_WaitForEvent. 377 */ 378 379 if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { 380 tclStubs.tcl_SetTimer(timePtr); 381 } 382} 383 384/* 385 *---------------------------------------------------------------------- 386 * 387 * Tcl_ServiceModeHook -- 388 * 389 * This function is invoked whenever the service mode changes. 390 * 391 * Results: 392 * None. 393 * 394 * Side effects: 395 * None. 396 * 397 *---------------------------------------------------------------------- 398 */ 399 400void 401Tcl_ServiceModeHook( 402 int mode) /* Either TCL_SERVICE_ALL, or 403 * TCL_SERVICE_NONE. */ 404{ 405} 406 407/* 408 *---------------------------------------------------------------------- 409 * 410 * Tcl_CreateFileHandler -- 411 * 412 * This function registers a file handler with the select notifier. 413 * 414 * Results: 415 * None. 416 * 417 * Side effects: 418 * Creates a new file handler structure. 419 * 420 *---------------------------------------------------------------------- 421 */ 422 423void 424Tcl_CreateFileHandler( 425 int fd, /* Handle of stream to watch. */ 426 int mask, /* OR'ed combination of TCL_READABLE, 427 * TCL_WRITABLE, and TCL_EXCEPTION: indicates 428 * conditions under which proc should be 429 * called. */ 430 Tcl_FileProc *proc, /* Function to call for each selected 431 * event. */ 432 ClientData clientData) /* Arbitrary data to pass to proc. */ 433{ 434 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 435 FileHandler *filePtr; 436 437 if (tclStubs.tcl_CreateFileHandler != 438 tclOriginalNotifier.createFileHandlerProc) { 439 tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData); 440 return; 441 } 442 443 for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; 444 filePtr = filePtr->nextPtr) { 445 if (filePtr->fd == fd) { 446 break; 447 } 448 } 449 if (filePtr == NULL) { 450 filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); 451 filePtr->fd = fd; 452 filePtr->readyMask = 0; 453 filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; 454 tsdPtr->firstFileHandlerPtr = filePtr; 455 } 456 filePtr->proc = proc; 457 filePtr->clientData = clientData; 458 filePtr->mask = mask; 459 460 /* 461 * Update the check masks for this file. 462 */ 463 464 if (mask & TCL_READABLE) { 465 FD_SET(fd, &(tsdPtr->checkMasks.readable)); 466 } else { 467 FD_CLR(fd, &(tsdPtr->checkMasks.readable)); 468 } 469 if (mask & TCL_WRITABLE) { 470 FD_SET(fd, &(tsdPtr->checkMasks.writable)); 471 } else { 472 FD_CLR(fd, &(tsdPtr->checkMasks.writable)); 473 } 474 if (mask & TCL_EXCEPTION) { 475 FD_SET(fd, &(tsdPtr->checkMasks.exceptional)); 476 } else { 477 FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); 478 } 479 if (tsdPtr->numFdBits <= fd) { 480 tsdPtr->numFdBits = fd+1; 481 } 482} 483 484/* 485 *---------------------------------------------------------------------- 486 * 487 * Tcl_DeleteFileHandler -- 488 * 489 * Cancel a previously-arranged callback arrangement for a file. 490 * 491 * Results: 492 * None. 493 * 494 * Side effects: 495 * If a callback was previously registered on file, remove it. 496 * 497 *---------------------------------------------------------------------- 498 */ 499 500void 501Tcl_DeleteFileHandler( 502 int fd) /* Stream id for which to remove callback 503 * function. */ 504{ 505 FileHandler *filePtr, *prevPtr; 506 int i; 507 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 508 509 if (tclStubs.tcl_DeleteFileHandler != 510 tclOriginalNotifier.deleteFileHandlerProc) { 511 tclStubs.tcl_DeleteFileHandler(fd); 512 return; 513 } 514 515 /* 516 * Find the entry for the given file (and return if there isn't one). 517 */ 518 519 for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; 520 prevPtr = filePtr, filePtr = filePtr->nextPtr) { 521 if (filePtr == NULL) { 522 return; 523 } 524 if (filePtr->fd == fd) { 525 break; 526 } 527 } 528 529 /* 530 * Update the check masks for this file. 531 */ 532 533 if (filePtr->mask & TCL_READABLE) { 534 FD_CLR(fd, &(tsdPtr->checkMasks.readable)); 535 } 536 if (filePtr->mask & TCL_WRITABLE) { 537 FD_CLR(fd, &(tsdPtr->checkMasks.writable)); 538 } 539 if (filePtr->mask & TCL_EXCEPTION) { 540 FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); 541 } 542 543 /* 544 * Find current max fd. 545 */ 546 547 if (fd+1 == tsdPtr->numFdBits) { 548 int numFdBits = 0; 549 550 for (i = fd-1; i >= 0; i--) { 551 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) 552 || FD_ISSET(i, &(tsdPtr->checkMasks.writable)) 553 || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { 554 numFdBits = i+1; 555 break; 556 } 557 } 558 tsdPtr->numFdBits = numFdBits; 559 } 560 561 /* 562 * Clean up information in the callback record. 563 */ 564 565 if (prevPtr == NULL) { 566 tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; 567 } else { 568 prevPtr->nextPtr = filePtr->nextPtr; 569 } 570 ckfree((char *) filePtr); 571} 572 573/* 574 *---------------------------------------------------------------------- 575 * 576 * FileHandlerEventProc -- 577 * 578 * This function is called by Tcl_ServiceEvent when a file event reaches 579 * the front of the event queue. This function is responsible for 580 * actually handling the event by invoking the callback for the file 581 * handler. 582 * 583 * Results: 584 * Returns 1 if the event was handled, meaning it should be removed from 585 * the queue. Returns 0 if the event was not handled, meaning it should 586 * stay on the queue. The only time the event isn't handled is if the 587 * TCL_FILE_EVENTS flag bit isn't set. 588 * 589 * Side effects: 590 * Whatever the file handler's callback function does. 591 * 592 *---------------------------------------------------------------------- 593 */ 594 595static int 596FileHandlerEventProc( 597 Tcl_Event *evPtr, /* Event to service. */ 598 int flags) /* Flags that indicate what events to handle, 599 * such as TCL_FILE_EVENTS. */ 600{ 601 int mask; 602 FileHandler *filePtr; 603 FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr; 604 ThreadSpecificData *tsdPtr; 605 606 if (!(flags & TCL_FILE_EVENTS)) { 607 return 0; 608 } 609 610 /* 611 * Search through the file handlers to find the one whose handle matches 612 * the event. We do this rather than keeping a pointer to the file handler 613 * directly in the event, so that the handler can be deleted while the 614 * event is queued without leaving a dangling pointer. 615 */ 616 617 tsdPtr = TCL_TSD_INIT(&dataKey); 618 for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; 619 filePtr = filePtr->nextPtr) { 620 if (filePtr->fd != fileEvPtr->fd) { 621 continue; 622 } 623 624 /* 625 * The code is tricky for two reasons: 626 * 1. The file handler's desired events could have changed since the 627 * time when the event was queued, so AND the ready mask with the 628 * desired mask. 629 * 2. The file could have been closed and re-opened since the time 630 * when the event was queued. This is why the ready mask is stored 631 * in the file handler rather than the queued event: it will be 632 * zeroed when a new file handler is created for the newly opened 633 * file. 634 */ 635 636 mask = filePtr->readyMask & filePtr->mask; 637 filePtr->readyMask = 0; 638 if (mask != 0) { 639 (*filePtr->proc)(filePtr->clientData, mask); 640 } 641 break; 642 } 643 return 1; 644} 645 646/* 647 *---------------------------------------------------------------------- 648 * 649 * Tcl_WaitForEvent -- 650 * 651 * This function is called by Tcl_DoOneEvent to wait for new events on 652 * the message queue. If the block time is 0, then Tcl_WaitForEvent just 653 * polls without blocking. 654 * 655 * Results: 656 * Returns -1 if the select would block forever, otherwise returns 0. 657 * 658 * Side effects: 659 * Queues file events that are detected by the select. 660 * 661 *---------------------------------------------------------------------- 662 */ 663 664int 665Tcl_WaitForEvent( 666 Tcl_Time *timePtr) /* Maximum block time, or NULL. */ 667{ 668 FileHandler *filePtr; 669 FileHandlerEvent *fileEvPtr; 670 int mask; 671 Tcl_Time vTime; 672#ifdef TCL_THREADS 673 int waitForFiles; 674#else 675 /* 676 * Impl. notes: timeout & timeoutPtr are used if, and only if threads are 677 * not enabled. They are the arguments for the regular select() used when 678 * the core is not thread-enabled. 679 */ 680 681 struct timeval timeout, *timeoutPtr; 682 int numFound; 683#endif /* TCL_THREADS */ 684 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 685 686 if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { 687 return tclStubs.tcl_WaitForEvent(timePtr); 688 } 689 690 /* 691 * Set up the timeout structure. Note that if there are no events to check 692 * for, we return with a negative result rather than blocking forever. 693 */ 694 695 if (timePtr != NULL) { 696 /* 697 * TIP #233 (Virtualized Time). Is virtual time in effect? And do we 698 * actually have something to scale? If yes to both then we call the 699 * handler to do this scaling. 700 */ 701 702 if (timePtr->sec != 0 || timePtr->usec != 0) { 703 vTime = *timePtr; 704 (*tclScaleTimeProcPtr) (&vTime, tclTimeClientData); 705 timePtr = &vTime; 706 } 707#ifndef TCL_THREADS 708 timeout.tv_sec = timePtr->sec; 709 timeout.tv_usec = timePtr->usec; 710 timeoutPtr = &timeout; 711 } else if (tsdPtr->numFdBits == 0) { 712 /* 713 * If there are no threads, no timeout, and no fds registered, then 714 * there are no events possible and we must avoid deadlock. Note that 715 * this is not entirely correct because there might be a signal that 716 * could interrupt the select call, but we don't handle that case if 717 * we aren't using threads. 718 */ 719 720 return -1; 721 } else { 722 timeoutPtr = NULL; 723#endif /* TCL_THREADS */ 724 } 725 726#ifdef TCL_THREADS 727 /* 728 * Place this thread on the list of interested threads, signal the 729 * notifier thread, and wait for a response or a timeout. 730 */ 731 732 Tcl_MutexLock(¬ifierMutex); 733 734 if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0 735#if defined(__APPLE__) && defined(__LP64__) 736 /* 737 * On 64-bit Darwin, pthread_cond_timedwait() appears to have a bug 738 * that causes it to wait forever when passed an absolute time which 739 * has already been exceeded by the system time; as a workaround, 740 * when given a very brief timeout, just do a poll. [Bug 1457797] 741 */ 742 || timePtr->usec < 10 743#endif 744 )) { 745 /* 746 * Cannot emulate a polling select with a polling condition variable. 747 * Instead, pretend to wait for files and tell the notifier thread 748 * what we are doing. The notifier thread makes sure it goes through 749 * select with its select mask in the same state as ours currently is. 750 * We block until that happens. 751 */ 752 753 waitForFiles = 1; 754 tsdPtr->pollState = POLL_WANT; 755 timePtr = NULL; 756 } else { 757 waitForFiles = (tsdPtr->numFdBits > 0); 758 tsdPtr->pollState = 0; 759 } 760 761 if (waitForFiles) { 762 /* 763 * Add the ThreadSpecificData structure of this thread to the list of 764 * ThreadSpecificData structures of all threads that are waiting on 765 * file events. 766 */ 767 768 tsdPtr->nextPtr = waitingListPtr; 769 if (waitingListPtr) { 770 waitingListPtr->prevPtr = tsdPtr; 771 } 772 tsdPtr->prevPtr = 0; 773 waitingListPtr = tsdPtr; 774 tsdPtr->onList = 1; 775 776 if (write(triggerPipe, "", 1) != 1) { 777 Tcl_Panic("Tcl_WaitForEvent: unable to write to triggerPipe"); 778 } 779 } 780 781 FD_ZERO(&(tsdPtr->readyMasks.readable)); 782 FD_ZERO(&(tsdPtr->readyMasks.writable)); 783 FD_ZERO(&(tsdPtr->readyMasks.exceptional)); 784 785 if (!tsdPtr->eventReady) { 786 Tcl_ConditionWait(&tsdPtr->waitCV, ¬ifierMutex, timePtr); 787 } 788 tsdPtr->eventReady = 0; 789 790 if (waitForFiles && tsdPtr->onList) { 791 /* 792 * Remove the ThreadSpecificData structure of this thread from the 793 * waiting list. Alert the notifier thread to recompute its select 794 * masks - skipping this caused a hang when trying to close a pipe 795 * which the notifier thread was still doing a select on. 796 */ 797 798 if (tsdPtr->prevPtr) { 799 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; 800 } else { 801 waitingListPtr = tsdPtr->nextPtr; 802 } 803 if (tsdPtr->nextPtr) { 804 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; 805 } 806 tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; 807 tsdPtr->onList = 0; 808 if (write(triggerPipe, "", 1) != 1) { 809 Tcl_Panic("Tcl_WaitForEvent: unable to write to triggerPipe"); 810 } 811 } 812 813#else 814 tsdPtr->readyMasks = tsdPtr->checkMasks; 815 numFound = select(tsdPtr->numFdBits, &(tsdPtr->readyMasks.readable), 816 &(tsdPtr->readyMasks.writable), &(tsdPtr->readyMasks.exceptional), 817 timeoutPtr); 818 819 /* 820 * Some systems don't clear the masks after an error, so we have to do it 821 * here. 822 */ 823 824 if (numFound == -1) { 825 FD_ZERO(&(tsdPtr->readyMasks.readable)); 826 FD_ZERO(&(tsdPtr->readyMasks.writable)); 827 FD_ZERO(&(tsdPtr->readyMasks.exceptional)); 828 } 829#endif /* TCL_THREADS */ 830 831 /* 832 * Queue all detected file events before returning. 833 */ 834 835 for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); 836 filePtr = filePtr->nextPtr) { 837 838 mask = 0; 839 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) { 840 mask |= TCL_READABLE; 841 } 842 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) { 843 mask |= TCL_WRITABLE; 844 } 845 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) { 846 mask |= TCL_EXCEPTION; 847 } 848 849 if (!mask) { 850 continue; 851 } 852 853 /* 854 * Don't bother to queue an event if the mask was previously non-zero 855 * since an event must still be on the queue. 856 */ 857 858 if (filePtr->readyMask == 0) { 859 fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent)); 860 fileEvPtr->header.proc = FileHandlerEventProc; 861 fileEvPtr->fd = filePtr->fd; 862 Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); 863 } 864 filePtr->readyMask = mask; 865 } 866#ifdef TCL_THREADS 867 Tcl_MutexUnlock(¬ifierMutex); 868#endif /* TCL_THREADS */ 869 return 0; 870} 871 872#ifdef TCL_THREADS 873/* 874 *---------------------------------------------------------------------- 875 * 876 * NotifierThreadProc -- 877 * 878 * This routine is the initial (and only) function executed by the 879 * special notifier thread. Its job is to wait for file descriptors to 880 * become readable or writable or to have an exception condition and then 881 * to notify other threads who are interested in this information by 882 * signalling a condition variable. Other threads can signal this 883 * notifier thread of a change in their interests by writing a single 884 * byte to a special pipe that the notifier thread is monitoring. 885 * 886 * Result: 887 * None. Once started, this routine never exits. It dies with the overall 888 * process. 889 * 890 * Side effects: 891 * The trigger pipe used to signal the notifier thread is created when 892 * the notifier thread first starts. 893 * 894 *---------------------------------------------------------------------- 895 */ 896 897static void 898NotifierThreadProc( 899 ClientData clientData) /* Not used. */ 900{ 901 ThreadSpecificData *tsdPtr; 902 fd_set readableMask; 903 fd_set writableMask; 904 fd_set exceptionalMask; 905 int fds[2]; 906 int i, numFdBits = 0, receivePipe; 907 long found; 908 struct timeval poll = {0., 0.}, *timePtr; 909 char buf[2]; 910 911 if (pipe(fds) != 0) { 912 Tcl_Panic("NotifierThreadProc: could not create trigger pipe"); 913 } 914 915 receivePipe = fds[0]; 916 917 if (TclUnixSetBlockingMode(receivePipe, TCL_MODE_NONBLOCKING) < 0) { 918 Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking"); 919 } 920 if (TclUnixSetBlockingMode(fds[1], TCL_MODE_NONBLOCKING) < 0) { 921 Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking"); 922 } 923 924 /* 925 * Install the write end of the pipe into the global variable. 926 */ 927 928 Tcl_MutexLock(¬ifierMutex); 929 triggerPipe = fds[1]; 930 931 /* 932 * Signal any threads that are waiting. 933 */ 934 935 Tcl_ConditionNotify(¬ifierCV); 936 Tcl_MutexUnlock(¬ifierMutex); 937 938 /* 939 * Look for file events and report them to interested threads. 940 */ 941 942 while (1) { 943 FD_ZERO(&readableMask); 944 FD_ZERO(&writableMask); 945 FD_ZERO(&exceptionalMask); 946 947 /* 948 * Compute the logical OR of the select masks from all the waiting 949 * notifiers. 950 */ 951 952 Tcl_MutexLock(¬ifierMutex); 953 timePtr = NULL; 954 for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { 955 for (i = tsdPtr->numFdBits-1; i >= 0; --i) { 956 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) { 957 FD_SET(i, &readableMask); 958 } 959 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) { 960 FD_SET(i, &writableMask); 961 } 962 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { 963 FD_SET(i, &exceptionalMask); 964 } 965 } 966 if (tsdPtr->numFdBits > numFdBits) { 967 numFdBits = tsdPtr->numFdBits; 968 } 969 if (tsdPtr->pollState & POLL_WANT) { 970 /* 971 * Here we make sure we go through select() with the same mask 972 * bits that were present when the thread tried to poll. 973 */ 974 975 tsdPtr->pollState |= POLL_DONE; 976 timePtr = &poll; 977 } 978 } 979 Tcl_MutexUnlock(¬ifierMutex); 980 981 /* 982 * Set up the select mask to include the receive pipe. 983 */ 984 985 if (receivePipe >= numFdBits) { 986 numFdBits = receivePipe + 1; 987 } 988 FD_SET(receivePipe, &readableMask); 989 990 if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask, 991 timePtr) == -1) { 992 /* 993 * Try again immediately on an error. 994 */ 995 996 continue; 997 } 998 999 /* 1000 * Alert any threads that are waiting on a ready file descriptor. 1001 */ 1002 1003 Tcl_MutexLock(¬ifierMutex); 1004 for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { 1005 found = 0; 1006 1007 for (i = tsdPtr->numFdBits-1; i >= 0; --i) { 1008 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) 1009 && FD_ISSET(i, &readableMask)) { 1010 FD_SET(i, &(tsdPtr->readyMasks.readable)); 1011 found = 1; 1012 } 1013 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable)) 1014 && FD_ISSET(i, &writableMask)) { 1015 FD_SET(i, &(tsdPtr->readyMasks.writable)); 1016 found = 1; 1017 } 1018 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional)) 1019 && FD_ISSET(i, &exceptionalMask)) { 1020 FD_SET(i, &(tsdPtr->readyMasks.exceptional)); 1021 found = 1; 1022 } 1023 } 1024 1025 if (found || (tsdPtr->pollState & POLL_DONE)) { 1026 tsdPtr->eventReady = 1; 1027 if (tsdPtr->onList) { 1028 /* 1029 * Remove the ThreadSpecificData structure of this thread 1030 * from the waiting list. This prevents us from 1031 * continuously spining on select until the other threads 1032 * runs and services the file event. 1033 */ 1034 1035 if (tsdPtr->prevPtr) { 1036 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; 1037 } else { 1038 waitingListPtr = tsdPtr->nextPtr; 1039 } 1040 if (tsdPtr->nextPtr) { 1041 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; 1042 } 1043 tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; 1044 tsdPtr->onList = 0; 1045 tsdPtr->pollState = 0; 1046 } 1047 Tcl_ConditionNotify(&tsdPtr->waitCV); 1048 } 1049 } 1050 Tcl_MutexUnlock(¬ifierMutex); 1051 1052 /* 1053 * Consume the next byte from the notifier pipe if the pipe was 1054 * readable. Note that there may be multiple bytes pending, but to 1055 * avoid a race condition we only read one at a time. 1056 */ 1057 1058 if (FD_ISSET(receivePipe, &readableMask)) { 1059 i = read(receivePipe, buf, 1); 1060 1061 if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) { 1062 /* 1063 * Someone closed the write end of the pipe or sent us a Quit 1064 * message [Bug: 4139] and then closed the write end of the 1065 * pipe so we need to shut down the notifier thread. 1066 */ 1067 1068 break; 1069 } 1070 } 1071 } 1072 1073 /* 1074 * Clean up the read end of the pipe and signal any threads waiting on 1075 * termination of the notifier thread. 1076 */ 1077 1078 close(receivePipe); 1079 Tcl_MutexLock(¬ifierMutex); 1080 triggerPipe = -1; 1081 Tcl_ConditionNotify(¬ifierCV); 1082 Tcl_MutexUnlock(¬ifierMutex); 1083 1084 TclpThreadExit (0); 1085} 1086#endif /* TCL_THREADS */ 1087 1088#endif /* HAVE_COREFOUNDATION */ 1089 1090/* 1091 * Local Variables: 1092 * mode: c 1093 * c-basic-offset: 4 1094 * fill-column: 78 1095 * End: 1096 */ 1097