1/* 2 * tclMacOSXNotify.c -- 3 * 4 * This file contains the implementation of a merged CFRunLoop/select() 5 * based notifier, which is the lowest-level part of the Tcl event loop. 6 * This file works together with generic/tclNotify.c. 7 * 8 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 9 * Copyright 2001, Apple Computer, Inc. 10 * Copyright (c) 2005-2008 Daniel A. Steffen <das@users.sourceforge.net> 11 * 12 * See the file "license.terms" for information on usage and redistribution of 13 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 14 * 15 * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.1.2.15 2008/03/11 23:52:35 das Exp $ 16 */ 17 18#include "tclInt.h" 19#include "tclPort.h" 20#ifdef HAVE_COREFOUNDATION /* Traditional unix select-based notifier is 21 * in tclUnixNotfy.c */ 22#include <CoreFoundation/CoreFoundation.h> 23#include <pthread.h> 24 25extern TclStubs tclStubs; 26extern Tcl_NotifierProcs tclOriginalNotifier; 27 28/* 29 * This structure is used to keep track of the notifier info for a registered 30 * file. 31 */ 32 33typedef struct FileHandler { 34 int fd; 35 int mask; /* Mask of desired events: TCL_READABLE, 36 * etc. */ 37 int readyMask; /* Mask of events that have been seen since 38 * the last time file handlers were invoked 39 * for this file. */ 40 Tcl_FileProc *proc; /* Function to call, in the style of 41 * Tcl_CreateFileHandler. */ 42 ClientData clientData; /* Argument to pass to proc. */ 43 struct FileHandler *nextPtr;/* Next in list of all files we care about. */ 44} FileHandler; 45 46/* 47 * The following structure is what is added to the Tcl event queue when file 48 * handlers are ready to fire. 49 */ 50 51typedef struct FileHandlerEvent { 52 Tcl_Event header; /* Information that is standard for all 53 * events. */ 54 int fd; /* File descriptor that is ready. Used to find 55 * the FileHandler structure for the file 56 * (can't point directly to the FileHandler 57 * structure because it could go away while 58 * the event is queued). */ 59} FileHandlerEvent; 60 61/* 62 * The following structure contains a set of select() masks to track readable, 63 * writable, and exceptional conditions. 64 */ 65 66typedef struct SelectMasks { 67 fd_set readable; 68 fd_set writable; 69 fd_set exceptional; 70} SelectMasks; 71 72/* 73 * The following static structure contains the state information for the 74 * select based implementation of the Tcl notifier. One of these structures is 75 * created for each thread that is using the notifier. 76 */ 77 78typedef struct ThreadSpecificData { 79 FileHandler *firstFileHandlerPtr; 80 /* Pointer to head of file handler list. */ 81 SelectMasks checkMasks; /* This structure is used to build up the 82 * masks to be used in the next call to 83 * select. Bits are set in response to calls 84 * to Tcl_CreateFileHandler. */ 85 SelectMasks readyMasks; /* This array reflects the readable/writable 86 * conditions that were found to exist by the 87 * last call to select. */ 88 int numFdBits; /* Number of valid bits in checkMasks (one 89 * more than highest fd for which 90 * Tcl_WatchFile has been called). */ 91 int onList; /* True if it is in this list */ 92 unsigned int pollState; /* pollState is used to implement a polling 93 * handshake between each thread and the 94 * notifier thread. Bits defined below. */ 95 struct ThreadSpecificData *nextPtr, *prevPtr; 96 /* All threads that are currently waiting on 97 * an event have their ThreadSpecificData 98 * structure on a doubly-linked listed formed 99 * from these pointers. You must hold the 100 * notifierLock before accessing these 101 * fields. */ 102 CFRunLoopSourceRef runLoopSource; 103 /* Any other thread alerts a notifier that an 104 * event is ready to be processed by signaling 105 * this CFRunLoopSource. */ 106 CFRunLoopRef runLoop; /* This thread's CFRunLoop, needs to be woken 107 * up whenever the runLoopSource is 108 * signaled. */ 109 int eventReady; /* True if an event is ready to be 110 * processed. */ 111} ThreadSpecificData; 112 113static Tcl_ThreadDataKey dataKey; 114 115/* 116 * The following static indicates the number of threads that have initialized 117 * notifiers. 118 * 119 * You must hold the notifierInitLock before accessing this variable. 120 */ 121 122static int notifierCount = 0; 123 124/* 125 * The following variable points to the head of a doubly-linked list of 126 * ThreadSpecificData structures for all threads that are currently waiting on 127 * an event. 128 * 129 * You must hold the notifierLock before accessing this list. 130 */ 131 132static ThreadSpecificData *waitingListPtr = NULL; 133 134/* 135 * The notifier thread spends all its time in select() waiting for a file 136 * descriptor associated with one of the threads on the waitingListPtr list to 137 * do something interesting. But if the contents of the waitingListPtr list 138 * ever changes, we need to wake up and restart the select() system call. You 139 * can wake up the notifier thread by writing a single byte to the file 140 * descriptor defined below. This file descriptor is the input-end of a pipe 141 * and the notifier thread is listening for data on the output-end of the same 142 * pipe. Hence writing to this file descriptor will cause the select() system 143 * call to return and wake up the notifier thread. 144 * 145 * You must hold the notifierLock lock before writing to the pipe. 146 */ 147 148static int triggerPipe = -1; 149static int receivePipe = -1; /* Output end of triggerPipe */ 150 151/* 152 * We use the Darwin-native spinlock API rather than pthread mutexes for 153 * notifier locking: this radically simplifies the implementation and lowers 154 * overhead. Note that these are not pure spinlocks, they employ various 155 * strategies to back off and relinquish the processor, making them immune to 156 * most priority-inversion livelocks (c.f. 'man 3 OSSpinLockLock' and Darwin 157 * sources: xnu/osfmk/{ppc,i386}/commpage/spinlocks.s). 158 */ 159 160#if defined(HAVE_LIBKERN_OSATOMIC_H) && defined(HAVE_OSSPINLOCKLOCK) 161/* 162 * Use OSSpinLock API where available (Tiger or later). 163 */ 164 165#include <libkern/OSAtomic.h> 166 167#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040 168/* 169 * Support for weakly importing spinlock API. 170 */ 171#define WEAK_IMPORT_SPINLOCKLOCK 172#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 173#define VOLATILE volatile 174#else 175#define VOLATILE 176#endif 177#ifndef bool 178#define bool int 179#endif 180extern void OSSpinLockLock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; 181extern void OSSpinLockUnlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; 182extern bool OSSpinLockTry(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; 183extern void _spin_lock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; 184extern void _spin_unlock(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; 185extern bool _spin_lock_try(VOLATILE OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE; 186static void (* lockLock)(VOLATILE OSSpinLock *lock) = NULL; 187static void (* lockUnlock)(VOLATILE OSSpinLock *lock) = NULL; 188static bool (* lockTry)(VOLATILE OSSpinLock *lock) = NULL; 189#undef VOLATILE 190static pthread_once_t spinLockLockInitControl = PTHREAD_ONCE_INIT; 191static void SpinLockLockInit(void) { 192 lockLock = OSSpinLockLock != NULL ? OSSpinLockLock : _spin_lock; 193 lockUnlock = OSSpinLockUnlock != NULL ? OSSpinLockUnlock : _spin_unlock; 194 lockTry = OSSpinLockTry != NULL ? OSSpinLockTry : _spin_lock_try; 195 if (lockLock == NULL || lockUnlock == NULL) { 196 Tcl_Panic("SpinLockLockInit: no spinlock API available"); 197 } 198} 199#define SpinLockLock(p) lockLock(p) 200#define SpinLockUnlock(p) lockUnlock(p) 201#define SpinLockTry(p) lockTry(p) 202#else 203#define SpinLockLock(p) OSSpinLockLock(p) 204#define SpinLockUnlock(p) OSSpinLockUnlock(p) 205#define SpinLockTry(p) OSSpinLockTry(p) 206#endif /* HAVE_WEAK_IMPORT */ 207#define SPINLOCK_INIT OS_SPINLOCK_INIT 208 209#else 210/* 211 * Otherwise, use commpage spinlock SPI directly. 212 */ 213 214typedef uint32_t OSSpinLock; 215extern void _spin_lock(OSSpinLock *lock); 216extern void _spin_unlock(OSSpinLock *lock); 217extern int _spin_lock_try(OSSpinLock *lock); 218#define SpinLockLock(p) _spin_lock(p) 219#define SpinLockUnlock(p) _spin_unlock(p) 220#define SpinLockTry(p) _spin_lock_try(p) 221#define SPINLOCK_INIT 0 222 223#endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */ 224 225/* 226 * These spinlocks lock access to the global notifier state. 227 */ 228 229static OSSpinLock notifierInitLock = SPINLOCK_INIT; 230static OSSpinLock notifierLock = SPINLOCK_INIT; 231 232/* 233 * Macros abstracting notifier locking/unlocking 234 */ 235 236#define LOCK_NOTIFIER_INIT SpinLockLock(¬ifierInitLock) 237#define UNLOCK_NOTIFIER_INIT SpinLockUnlock(¬ifierInitLock) 238#define LOCK_NOTIFIER SpinLockLock(¬ifierLock) 239#define UNLOCK_NOTIFIER SpinLockUnlock(¬ifierLock) 240 241/* 242 * The pollState bits 243 * POLL_WANT is set by each thread before it waits on its condition 244 * variable. It is checked by the notifier before it does select. 245 * POLL_DONE is set by the notifier if it goes into select after seeing 246 * POLL_WANT. The idea is to ensure it tries a select with the 247 * same bits the initial thread had set. 248 */ 249 250#define POLL_WANT 0x1 251#define POLL_DONE 0x2 252 253/* 254 * This is the thread ID of the notifier thread that does select. 255 */ 256 257static pthread_t notifierThread; 258 259/* 260 * Custom run loop mode containing only the run loop source for the 261 * notifier thread. 262 */ 263 264#ifndef TCL_EVENTS_ONLY_RUN_LOOP_MODE 265#define TCL_EVENTS_ONLY_RUN_LOOP_MODE "com.tcltk.tclEventsOnlyRunLoopMode" 266#endif 267#ifdef __CONSTANT_CFSTRINGS__ 268#define tclEventsOnlyRunLoopMode CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE) 269#else 270static CFStringRef tclEventsOnlyRunLoopMode = NULL; 271#endif 272 273/* 274 * Static routines defined in this file. 275 */ 276 277static void NotifierThreadProc(ClientData clientData) 278 __attribute__ ((__noreturn__)); 279static int FileHandlerEventProc(Tcl_Event *evPtr, int flags); 280 281#ifdef HAVE_PTHREAD_ATFORK 282static int atForkInit = 0; 283static void AtForkPrepare(void); 284static void AtForkParent(void); 285static void AtForkChild(void); 286#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040 287/* Support for weakly importing pthread_atfork. */ 288#define WEAK_IMPORT_PTHREAD_ATFORK 289extern int pthread_atfork(void (*prepare)(void), void (*parent)(void), 290 void (*child)(void)) WEAK_IMPORT_ATTRIBUTE; 291#endif /* HAVE_WEAK_IMPORT */ 292/* 293 * On Darwin 9 and later, it is not possible to call CoreFoundation after 294 * a fork. 295 */ 296#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \ 297 MAC_OS_X_VERSION_MIN_REQUIRED < 1050 298MODULE_SCOPE long tclMacOSXDarwinRelease; 299#define noCFafterFork (tclMacOSXDarwinRelease >= 9) 300#else /* MAC_OS_X_VERSION_MIN_REQUIRED */ 301#define noCFafterFork 1 302#endif /* MAC_OS_X_VERSION_MIN_REQUIRED */ 303#endif /* HAVE_PTHREAD_ATFORK */ 304 305/* 306 *---------------------------------------------------------------------- 307 * 308 * Tcl_InitNotifier -- 309 * 310 * Initializes the platform specific notifier state. 311 * 312 * Results: 313 * Returns a handle to the notifier state for this thread. 314 * 315 * Side effects: 316 * None. 317 * 318 *---------------------------------------------------------------------- 319 */ 320 321ClientData 322Tcl_InitNotifier(void) 323{ 324 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 325 326 tsdPtr->eventReady = 0; 327 328#ifdef WEAK_IMPORT_SPINLOCKLOCK 329 /* 330 * Initialize support for weakly imported spinlock API. 331 */ 332 if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) { 333 Tcl_Panic("Tcl_InitNotifier: pthread_once failed"); 334 } 335#endif 336 337#ifndef __CONSTANT_CFSTRINGS__ 338 if (!tclEventsOnlyRunLoopMode) { 339 tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE); 340 } 341#endif 342 343 /* 344 * Initialize CFRunLoopSource and add it to CFRunLoop of this thread. 345 */ 346 347 if (!tsdPtr->runLoop) { 348 CFRunLoopRef runLoop = CFRunLoopGetCurrent(); 349 CFRunLoopSourceRef runLoopSource; 350 CFRunLoopSourceContext runLoopSourceContext; 351 352 bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext)); 353 runLoopSourceContext.info = tsdPtr; 354 runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext); 355 if (!runLoopSource) { 356 Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource"); 357 } 358 CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes); 359 CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode); 360 tsdPtr->runLoopSource = runLoopSource; 361 tsdPtr->runLoop = runLoop; 362 } 363 364 LOCK_NOTIFIER_INIT; 365#ifdef HAVE_PTHREAD_ATFORK 366 /* 367 * Install pthread_atfork handlers to reinitialize the notifier in the 368 * child of a fork. 369 */ 370 371 if ( 372#ifdef WEAK_IMPORT_PTHREAD_ATFORK 373 pthread_atfork != NULL && 374#endif 375 !atForkInit) { 376 int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild); 377 if (result) { 378 Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed"); 379 } 380 atForkInit = 1; 381 } 382#endif 383 if (notifierCount == 0) { 384 int fds[2], status; 385 386 /* 387 * Initialize trigger pipe. 388 */ 389 390 if (pipe(fds) != 0) { 391 Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe"); 392 } 393 394 status = fcntl(fds[0], F_GETFL); 395 status |= O_NONBLOCK; 396 if (fcntl(fds[0], F_SETFL, status) < 0) { 397 Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non blocking"); 398 } 399 status = fcntl(fds[1], F_GETFL); 400 status |= O_NONBLOCK; 401 if (fcntl(fds[1], F_SETFL, status) < 0) { 402 Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non blocking"); 403 } 404 405 receivePipe = fds[0]; 406 triggerPipe = fds[1]; 407 408 /* 409 * Create notifier thread lazily in Tcl_WaitForEvent() to avoid 410 * interfering with fork() followed immediately by execve() 411 * (cannot execve() when more than one thread is present). 412 */ 413 414 notifierThread = 0; 415 } 416 notifierCount++; 417 UNLOCK_NOTIFIER_INIT; 418 419 return (ClientData) tsdPtr; 420} 421 422/* 423 *---------------------------------------------------------------------- 424 * 425 * Tcl_FinalizeNotifier -- 426 * 427 * This function is called to cleanup the notifier state before a thread 428 * is terminated. 429 * 430 * Results: 431 * None. 432 * 433 * Side effects: 434 * May terminate the background notifier thread if this is the last 435 * notifier instance. 436 * 437 *---------------------------------------------------------------------- 438 */ 439 440void 441Tcl_FinalizeNotifier( 442 ClientData clientData) /* Not used. */ 443{ 444 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 445 446 LOCK_NOTIFIER_INIT; 447 notifierCount--; 448 449 /* 450 * If this is the last thread to use the notifier, close the notifier pipe 451 * and wait for the background thread to terminate. 452 */ 453 454 if (notifierCount == 0) { 455 int result; 456 457 if (triggerPipe < 0) { 458 Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized"); 459 } 460 461 /* 462 * Send "q" message to the notifier thread so that it will terminate. 463 * The notifier will return from its call to select() and notice that 464 * a "q" message has arrived, it will then close its side of the pipe 465 * and terminate its thread. Note the we can not just close the pipe 466 * and check for EOF in the notifier thread because if a background 467 * child process was created with exec, select() would not register 468 * the EOF on the pipe until the child processes had terminated. [Bug: 469 * 4139] [Bug: 1222872] 470 */ 471 472 write(triggerPipe, "q", 1); 473 close(triggerPipe); 474 475 if (notifierThread) { 476 result = pthread_join(notifierThread, NULL); 477 if (result) { 478 Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); 479 } 480 notifierThread = 0; 481 } 482 483 close(receivePipe); 484 triggerPipe = -1; 485 } 486 UNLOCK_NOTIFIER_INIT; 487 488 LOCK_NOTIFIER; /* for concurrency with Tcl_AlertNotifier */ 489 if (tsdPtr->runLoop) { 490 tsdPtr->runLoop = NULL; 491 492 /* 493 * Remove runLoopSource from all CFRunLoops and release it. 494 */ 495 496 CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); 497 CFRelease(tsdPtr->runLoopSource); 498 tsdPtr->runLoopSource = NULL; 499 } 500 UNLOCK_NOTIFIER; 501} 502 503/* 504 *---------------------------------------------------------------------- 505 * 506 * Tcl_AlertNotifier -- 507 * 508 * Wake up the specified notifier from any thread. This routine is called 509 * by the platform independent notifier code whenever the Tcl_ThreadAlert 510 * routine is called. This routine is guaranteed not to be called on a 511 * given notifier after Tcl_FinalizeNotifier is called for that notifier. 512 * 513 * Results: 514 * None. 515 * 516 * Side effects: 517 * Signals the notifier condition variable for the specified notifier. 518 * 519 *---------------------------------------------------------------------- 520 */ 521 522void 523Tcl_AlertNotifier( 524 ClientData clientData) 525{ 526 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; 527 528 LOCK_NOTIFIER; 529 if (tsdPtr->runLoop) { 530 tsdPtr->eventReady = 1; 531 CFRunLoopSourceSignal(tsdPtr->runLoopSource); 532 CFRunLoopWakeUp(tsdPtr->runLoop); 533 } 534 UNLOCK_NOTIFIER; 535} 536 537/* 538 *---------------------------------------------------------------------- 539 * 540 * Tcl_SetTimer -- 541 * 542 * This function sets the current notifier timer value. This interface is 543 * not implemented in this notifier because we are always running inside 544 * of Tcl_DoOneEvent. 545 * 546 * Results: 547 * None. 548 * 549 * Side effects: 550 * None. 551 * 552 *---------------------------------------------------------------------- 553 */ 554 555void 556Tcl_SetTimer( 557 Tcl_Time *timePtr) /* Timeout value, may be NULL. */ 558{ 559 /* 560 * The interval timer doesn't do anything in this implementation, because 561 * the only event loop is via Tcl_DoOneEvent, which passes timeout values 562 * to Tcl_WaitForEvent. 563 */ 564 565 if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { 566 tclStubs.tcl_SetTimer(timePtr); 567 } 568} 569 570/* 571 *---------------------------------------------------------------------- 572 * 573 * Tcl_ServiceModeHook -- 574 * 575 * This function is invoked whenever the service mode changes. 576 * 577 * Results: 578 * None. 579 * 580 * Side effects: 581 * None. 582 * 583 *---------------------------------------------------------------------- 584 */ 585 586void 587Tcl_ServiceModeHook( 588 int mode) /* Either TCL_SERVICE_ALL, or 589 * TCL_SERVICE_NONE. */ 590{ 591} 592 593/* 594 *---------------------------------------------------------------------- 595 * 596 * Tcl_CreateFileHandler -- 597 * 598 * This function registers a file handler with the select notifier. 599 * 600 * Results: 601 * None. 602 * 603 * Side effects: 604 * Creates a new file handler structure. 605 * 606 *---------------------------------------------------------------------- 607 */ 608 609void 610Tcl_CreateFileHandler( 611 int fd, /* Handle of stream to watch. */ 612 int mask, /* OR'ed combination of TCL_READABLE, 613 * TCL_WRITABLE, and TCL_EXCEPTION: indicates 614 * conditions under which proc should be 615 * called. */ 616 Tcl_FileProc *proc, /* Function to call for each selected 617 * event. */ 618 ClientData clientData) /* Arbitrary data to pass to proc. */ 619{ 620 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 621 FileHandler *filePtr; 622 623 if (tclStubs.tcl_CreateFileHandler != 624 tclOriginalNotifier.createFileHandlerProc) { 625 tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData); 626 return; 627 } 628 629 for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; 630 filePtr = filePtr->nextPtr) { 631 if (filePtr->fd == fd) { 632 break; 633 } 634 } 635 if (filePtr == NULL) { 636 filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); 637 filePtr->fd = fd; 638 filePtr->readyMask = 0; 639 filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; 640 tsdPtr->firstFileHandlerPtr = filePtr; 641 } 642 filePtr->proc = proc; 643 filePtr->clientData = clientData; 644 filePtr->mask = mask; 645 646 /* 647 * Update the check masks for this file. 648 */ 649 650 if (mask & TCL_READABLE) { 651 FD_SET(fd, &(tsdPtr->checkMasks.readable)); 652 } else { 653 FD_CLR(fd, &(tsdPtr->checkMasks.readable)); 654 } 655 if (mask & TCL_WRITABLE) { 656 FD_SET(fd, &(tsdPtr->checkMasks.writable)); 657 } else { 658 FD_CLR(fd, &(tsdPtr->checkMasks.writable)); 659 } 660 if (mask & TCL_EXCEPTION) { 661 FD_SET(fd, &(tsdPtr->checkMasks.exceptional)); 662 } else { 663 FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); 664 } 665 if (tsdPtr->numFdBits <= fd) { 666 tsdPtr->numFdBits = fd+1; 667 } 668} 669 670/* 671 *---------------------------------------------------------------------- 672 * 673 * Tcl_DeleteFileHandler -- 674 * 675 * Cancel a previously-arranged callback arrangement for a file. 676 * 677 * Results: 678 * None. 679 * 680 * Side effects: 681 * If a callback was previously registered on file, remove it. 682 * 683 *---------------------------------------------------------------------- 684 */ 685 686void 687Tcl_DeleteFileHandler( 688 int fd) /* Stream id for which to remove callback 689 * function. */ 690{ 691 FileHandler *filePtr, *prevPtr; 692 int i; 693 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 694 695 if (tclStubs.tcl_DeleteFileHandler != 696 tclOriginalNotifier.deleteFileHandlerProc) { 697 tclStubs.tcl_DeleteFileHandler(fd); 698 return; 699 } 700 701 /* 702 * Find the entry for the given file (and return if there isn't one). 703 */ 704 705 for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; 706 prevPtr = filePtr, filePtr = filePtr->nextPtr) { 707 if (filePtr == NULL) { 708 return; 709 } 710 if (filePtr->fd == fd) { 711 break; 712 } 713 } 714 715 /* 716 * Update the check masks for this file. 717 */ 718 719 if (filePtr->mask & TCL_READABLE) { 720 FD_CLR(fd, &(tsdPtr->checkMasks.readable)); 721 } 722 if (filePtr->mask & TCL_WRITABLE) { 723 FD_CLR(fd, &(tsdPtr->checkMasks.writable)); 724 } 725 if (filePtr->mask & TCL_EXCEPTION) { 726 FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); 727 } 728 729 /* 730 * Find current max fd. 731 */ 732 733 if (fd+1 == tsdPtr->numFdBits) { 734 tsdPtr->numFdBits = 0; 735 for (i = fd-1; i >= 0; i--) { 736 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) 737 || FD_ISSET(i, &(tsdPtr->checkMasks.writable)) 738 || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { 739 tsdPtr->numFdBits = i+1; 740 break; 741 } 742 } 743 } 744 745 /* 746 * Clean up information in the callback record. 747 */ 748 749 if (prevPtr == NULL) { 750 tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; 751 } else { 752 prevPtr->nextPtr = filePtr->nextPtr; 753 } 754 ckfree((char *) filePtr); 755} 756 757/* 758 *---------------------------------------------------------------------- 759 * 760 * FileHandlerEventProc -- 761 * 762 * This function is called by Tcl_ServiceEvent when a file event reaches 763 * the front of the event queue. This function is responsible for 764 * actually handling the event by invoking the callback for the file 765 * handler. 766 * 767 * Results: 768 * Returns 1 if the event was handled, meaning it should be removed from 769 * the queue. Returns 0 if the event was not handled, meaning it should 770 * stay on the queue. The only time the event isn't handled is if the 771 * TCL_FILE_EVENTS flag bit isn't set. 772 * 773 * Side effects: 774 * Whatever the file handler's callback function does. 775 * 776 *---------------------------------------------------------------------- 777 */ 778 779static int 780FileHandlerEventProc( 781 Tcl_Event *evPtr, /* Event to service. */ 782 int flags) /* Flags that indicate what events to handle, 783 * such as TCL_FILE_EVENTS. */ 784{ 785 int mask; 786 FileHandler *filePtr; 787 FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr; 788 ThreadSpecificData *tsdPtr; 789 790 if (!(flags & TCL_FILE_EVENTS)) { 791 return 0; 792 } 793 794 /* 795 * Search through the file handlers to find the one whose handle matches 796 * the event. We do this rather than keeping a pointer to the file handler 797 * directly in the event, so that the handler can be deleted while the 798 * event is queued without leaving a dangling pointer. 799 */ 800 801 tsdPtr = TCL_TSD_INIT(&dataKey); 802 for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; 803 filePtr = filePtr->nextPtr) { 804 if (filePtr->fd != fileEvPtr->fd) { 805 continue; 806 } 807 808 /* 809 * The code is tricky for two reasons: 810 * 1. The file handler's desired events could have changed since the 811 * time when the event was queued, so AND the ready mask with the 812 * desired mask. 813 * 2. The file could have been closed and re-opened since the time 814 * when the event was queued. This is why the ready mask is stored 815 * in the file handler rather than the queued event: it will be 816 * zeroed when a new file handler is created for the newly opened 817 * file. 818 */ 819 820 mask = filePtr->readyMask & filePtr->mask; 821 filePtr->readyMask = 0; 822 if (mask != 0) { 823 (*filePtr->proc)(filePtr->clientData, mask); 824 } 825 break; 826 } 827 return 1; 828} 829 830/* 831 *---------------------------------------------------------------------- 832 * 833 * Tcl_WaitForEvent -- 834 * 835 * This function is called by Tcl_DoOneEvent to wait for new events on 836 * the message queue. If the block time is 0, then Tcl_WaitForEvent just 837 * polls without blocking. 838 * 839 * Results: 840 * Returns -1 if the select would block forever, otherwise returns 0. 841 * 842 * Side effects: 843 * Queues file events that are detected by the select. 844 * 845 *---------------------------------------------------------------------- 846 */ 847 848int 849Tcl_WaitForEvent( 850 Tcl_Time *timePtr) /* Maximum block time, or NULL. */ 851{ 852 FileHandler *filePtr; 853 FileHandlerEvent *fileEvPtr; 854 int mask; 855 int waitForFiles; 856 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 857 858 if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { 859 return tclStubs.tcl_WaitForEvent(timePtr); 860 } 861 862 /* 863 * Start notifier thread if necessary. 864 */ 865 866 LOCK_NOTIFIER_INIT; 867 if (!notifierCount) { 868 Tcl_Panic("Tcl_WaitForEvent: notifier not initialized"); 869 } 870 if (!notifierThread) { 871 int result; 872 pthread_attr_t attr; 873 874 pthread_attr_init(&attr); 875 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 876 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 877 pthread_attr_setstacksize(&attr, 60 * 1024); 878 result = pthread_create(¬ifierThread, &attr, 879 (void * (*)(void *))NotifierThreadProc, NULL); 880 pthread_attr_destroy(&attr); 881 if (result || !notifierThread) { 882 Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread"); 883 } 884 } 885 UNLOCK_NOTIFIER_INIT; 886 887 /* 888 * Place this thread on the list of interested threads, signal the 889 * notifier thread, and wait for a response or a timeout. 890 */ 891 892 LOCK_NOTIFIER; 893 if (!tsdPtr->runLoop) { 894 Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized"); 895 } 896 waitForFiles = (tsdPtr->numFdBits > 0); 897 if (timePtr != NULL && timePtr->sec == 0 && timePtr->usec == 0) { 898 /* 899 * Cannot emulate a polling select with a polling condition variable. 900 * Instead, pretend to wait for files and tell the notifier thread 901 * what we are doing. The notifier thread makes sure it goes through 902 * select with its select mask in the same state as ours currently is. 903 * We block until that happens. 904 */ 905 906 waitForFiles = 1; 907 tsdPtr->pollState = POLL_WANT; 908 timePtr = NULL; 909 } else { 910 tsdPtr->pollState = 0; 911 } 912 913 if (waitForFiles) { 914 /* 915 * Add the ThreadSpecificData structure of this thread to the list of 916 * ThreadSpecificData structures of all threads that are waiting on 917 * file events. 918 */ 919 920 tsdPtr->nextPtr = waitingListPtr; 921 if (waitingListPtr) { 922 waitingListPtr->prevPtr = tsdPtr; 923 } 924 tsdPtr->prevPtr = 0; 925 waitingListPtr = tsdPtr; 926 tsdPtr->onList = 1; 927 928 write(triggerPipe, "", 1); 929 } 930 931 FD_ZERO(&(tsdPtr->readyMasks.readable)); 932 FD_ZERO(&(tsdPtr->readyMasks.writable)); 933 FD_ZERO(&(tsdPtr->readyMasks.exceptional)); 934 935 if (!tsdPtr->eventReady) { 936 CFTimeInterval waitTime; 937 CFStringRef runLoopMode; 938 939 if (timePtr == NULL) { 940 waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */ 941 } else { 942 waitTime = timePtr->sec + 1.0e-6 * timePtr->usec; 943 } 944 /* 945 * If the run loop is already running (e.g. if Tcl_WaitForEvent was 946 * called recursively), re-run it in a custom run loop mode containing 947 * only the source for the notifier thread, otherwise wakeups from other 948 * sources added to the common run loop modes might get lost. 949 */ 950 if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) { 951 CFRelease(runLoopMode); 952 runLoopMode = tclEventsOnlyRunLoopMode; 953 } else { 954 runLoopMode = kCFRunLoopDefaultMode; 955 } 956 UNLOCK_NOTIFIER; 957 CFRunLoopRunInMode(runLoopMode, waitTime, TRUE); 958 LOCK_NOTIFIER; 959 } 960 tsdPtr->eventReady = 0; 961 962 if (waitForFiles && tsdPtr->onList) { 963 /* 964 * Remove the ThreadSpecificData structure of this thread from the 965 * waiting list. Alert the notifier thread to recompute its select 966 * masks - skipping this caused a hang when trying to close a pipe 967 * which the notifier thread was still doing a select on. 968 */ 969 970 if (tsdPtr->prevPtr) { 971 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; 972 } else { 973 waitingListPtr = tsdPtr->nextPtr; 974 } 975 if (tsdPtr->nextPtr) { 976 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; 977 } 978 tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; 979 tsdPtr->onList = 0; 980 write(triggerPipe, "", 1); 981 } 982 983 /* 984 * Queue all detected file events before returning. 985 */ 986 987 for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); 988 filePtr = filePtr->nextPtr) { 989 990 mask = 0; 991 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) { 992 mask |= TCL_READABLE; 993 } 994 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) { 995 mask |= TCL_WRITABLE; 996 } 997 if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) { 998 mask |= TCL_EXCEPTION; 999 } 1000 1001 if (!mask) { 1002 continue; 1003 } 1004 1005 /* 1006 * Don't bother to queue an event if the mask was previously non-zero 1007 * since an event must still be on the queue. 1008 */ 1009 1010 if (filePtr->readyMask == 0) { 1011 fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent)); 1012 fileEvPtr->header.proc = FileHandlerEventProc; 1013 fileEvPtr->fd = filePtr->fd; 1014 Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); 1015 } 1016 filePtr->readyMask = mask; 1017 } 1018 UNLOCK_NOTIFIER; 1019 return 0; 1020} 1021 1022/* 1023 *---------------------------------------------------------------------- 1024 * 1025 * NotifierThreadProc -- 1026 * 1027 * This routine is the initial (and only) function executed by the 1028 * special notifier thread. Its job is to wait for file descriptors to 1029 * become readable or writable or to have an exception condition and then 1030 * to notify other threads who are interested in this information by 1031 * signalling a condition variable. Other threads can signal this 1032 * notifier thread of a change in their interests by writing a single 1033 * byte to a special pipe that the notifier thread is monitoring. 1034 * 1035 * Result: 1036 * None. Once started, this routine never exits. It dies with the overall 1037 * process. 1038 * 1039 * Side effects: 1040 * The trigger pipe used to signal the notifier thread is created when 1041 * the notifier thread first starts. 1042 * 1043 *---------------------------------------------------------------------- 1044 */ 1045 1046static void 1047NotifierThreadProc( 1048 ClientData clientData) /* Not used. */ 1049{ 1050 ThreadSpecificData *tsdPtr; 1051 fd_set readableMask; 1052 fd_set writableMask; 1053 fd_set exceptionalMask; 1054 int i, numFdBits = 0; 1055 long found; 1056 struct timeval poll = {0., 0.}, *timePtr; 1057 char buf[2]; 1058 1059 /* 1060 * Look for file events and report them to interested threads. 1061 */ 1062 1063 while (1) { 1064 FD_ZERO(&readableMask); 1065 FD_ZERO(&writableMask); 1066 FD_ZERO(&exceptionalMask); 1067 1068 /* 1069 * Compute the logical OR of the select masks from all the waiting 1070 * notifiers. 1071 */ 1072 1073 LOCK_NOTIFIER; 1074 timePtr = NULL; 1075 for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { 1076 for (i = tsdPtr->numFdBits-1; i >= 0; --i) { 1077 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) { 1078 FD_SET(i, &readableMask); 1079 } 1080 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) { 1081 FD_SET(i, &writableMask); 1082 } 1083 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { 1084 FD_SET(i, &exceptionalMask); 1085 } 1086 } 1087 if (tsdPtr->numFdBits > numFdBits) { 1088 numFdBits = tsdPtr->numFdBits; 1089 } 1090 if (tsdPtr->pollState & POLL_WANT) { 1091 /* 1092 * Here we make sure we go through select() with the same mask 1093 * bits that were present when the thread tried to poll. 1094 */ 1095 1096 tsdPtr->pollState |= POLL_DONE; 1097 timePtr = &poll; 1098 } 1099 } 1100 UNLOCK_NOTIFIER; 1101 1102 /* 1103 * Set up the select mask to include the receive pipe. 1104 */ 1105 1106 if (receivePipe >= numFdBits) { 1107 numFdBits = receivePipe + 1; 1108 } 1109 FD_SET(receivePipe, &readableMask); 1110 1111 if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask, 1112 timePtr) == -1) { 1113 /* 1114 * Try again immediately on an error. 1115 */ 1116 1117 continue; 1118 } 1119 1120 /* 1121 * Alert any threads that are waiting on a ready file descriptor. 1122 */ 1123 1124 LOCK_NOTIFIER; 1125 for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { 1126 found = 0; 1127 1128 for (i = tsdPtr->numFdBits-1; i >= 0; --i) { 1129 if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) 1130 && FD_ISSET(i, &readableMask)) { 1131 FD_SET(i, &(tsdPtr->readyMasks.readable)); 1132 found = 1; 1133 } 1134 if (FD_ISSET(i, &(tsdPtr->checkMasks.writable)) 1135 && FD_ISSET(i, &writableMask)) { 1136 FD_SET(i, &(tsdPtr->readyMasks.writable)); 1137 found = 1; 1138 } 1139 if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional)) 1140 && FD_ISSET(i, &exceptionalMask)) { 1141 FD_SET(i, &(tsdPtr->readyMasks.exceptional)); 1142 found = 1; 1143 } 1144 } 1145 1146 if (found || (tsdPtr->pollState & POLL_DONE)) { 1147 tsdPtr->eventReady = 1; 1148 if (tsdPtr->onList) { 1149 /* 1150 * Remove the ThreadSpecificData structure of this thread 1151 * from the waiting list. This prevents us from 1152 * continuously spining on select until the other threads 1153 * runs and services the file event. 1154 */ 1155 1156 if (tsdPtr->prevPtr) { 1157 tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; 1158 } else { 1159 waitingListPtr = tsdPtr->nextPtr; 1160 } 1161 if (tsdPtr->nextPtr) { 1162 tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; 1163 } 1164 tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; 1165 tsdPtr->onList = 0; 1166 tsdPtr->pollState = 0; 1167 } 1168 if (tsdPtr->runLoop) { 1169 CFRunLoopSourceSignal(tsdPtr->runLoopSource); 1170 CFRunLoopWakeUp(tsdPtr->runLoop); 1171 } 1172 } 1173 } 1174 UNLOCK_NOTIFIER; 1175 1176 /* 1177 * Consume the next byte from the notifier pipe if the pipe was 1178 * readable. Note that there may be multiple bytes pending, but to 1179 * avoid a race condition we only read one at a time. 1180 */ 1181 1182 if (FD_ISSET(receivePipe, &readableMask)) { 1183 i = read(receivePipe, buf, 1); 1184 1185 if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) { 1186 /* 1187 * Someone closed the write end of the pipe or sent us a Quit 1188 * message [Bug: 4139] and then closed the write end of the 1189 * pipe so we need to shut down the notifier thread. 1190 */ 1191 1192 break; 1193 } 1194 } 1195 } 1196 pthread_exit(0); 1197} 1198 1199#ifdef HAVE_PTHREAD_ATFORK 1200/* 1201 *---------------------------------------------------------------------- 1202 * 1203 * AtForkPrepare -- 1204 * 1205 * Lock the notifier in preparation for a fork. 1206 * 1207 * Results: 1208 * None. 1209 * 1210 * Side effects: 1211 * None. 1212 * 1213 *---------------------------------------------------------------------- 1214 */ 1215 1216static void 1217AtForkPrepare(void) 1218{ 1219 LOCK_NOTIFIER_INIT; 1220 LOCK_NOTIFIER; 1221} 1222 1223/* 1224 *---------------------------------------------------------------------- 1225 * 1226 * AtForkParent -- 1227 * 1228 * Unlock the notifier in the parent after a fork. 1229 * 1230 * Results: 1231 * None. 1232 * 1233 * Side effects: 1234 * None. 1235 * 1236 *---------------------------------------------------------------------- 1237 */ 1238 1239static void 1240AtForkParent(void) 1241{ 1242 UNLOCK_NOTIFIER; 1243 UNLOCK_NOTIFIER_INIT; 1244} 1245 1246/* 1247 *---------------------------------------------------------------------- 1248 * 1249 * AtForkChild -- 1250 * 1251 * Unlock and reinstall the notifier in the child after a fork. 1252 * 1253 * Results: 1254 * None. 1255 * 1256 * Side effects: 1257 * None. 1258 * 1259 *---------------------------------------------------------------------- 1260 */ 1261 1262static void 1263AtForkChild(void) 1264{ 1265 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 1266 1267 UNLOCK_NOTIFIER; 1268 UNLOCK_NOTIFIER_INIT; 1269 if (tsdPtr->runLoop) { 1270 tsdPtr->runLoop = NULL; 1271 if (!noCFafterFork) { 1272 CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); 1273 } 1274 CFRelease(tsdPtr->runLoopSource); 1275 tsdPtr->runLoopSource = NULL; 1276 } 1277 if (notifierCount > 0) { 1278 notifierCount = 0; 1279 1280 /* 1281 * Assume that the return value of Tcl_InitNotifier in the child will 1282 * be identical to the one stored as clientData in tclNotify.c's 1283 * ThreadSpecificData by the parent's TclInitNotifier, so discard the 1284 * return value here. This assumption may require the fork() to be 1285 * executed in the main thread of the parent, otherwise 1286 * Tcl_AlertNotifier may break in the child. 1287 */ 1288 1289 if (!noCFafterFork) { 1290 Tcl_InitNotifier(); 1291 } 1292 } 1293} 1294#endif /* HAVE_PTHREAD_ATFORK */ 1295 1296#endif /* HAVE_COREFOUNDATION */ 1297