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