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