os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclUnixNotfy.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclUnixNotfy.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1096 @@
1.4 +/*
1.5 + * tclUnixNotify.c --
1.6 + *
1.7 + * This file contains the implementation of the select-based
1.8 + * Unix-specific notifier, which is the lowest-level part of the
1.9 + * Tcl event loop. This file works together with
1.10 + * ../generic/tclNotify.c.
1.11 + *
1.12 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.13 + * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
1.14 + *
1.15 + * See the file "license.terms" for information on usage and redistribution
1.16 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.17 + *
1.18 + * RCS: @(#) $Id: tclUnixNotfy.c,v 1.11.2.16 2006/08/22 17:45:02 andreas_kupries Exp $
1.19 + */
1.20 +
1.21 +#include "tclInt.h"
1.22 +#include "tclPort.h"
1.23 +#if defined(__SYMBIAN32__) && defined(__WINSCW__)
1.24 +#include "tclSymbianGlobals.h"
1.25 +#define dataKey getdataKey(8)
1.26 +#endif
1.27 +
1.28 +#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier
1.29 + * is in tclMacOSXNotify.c */
1.30 +#ifndef __SYMBIAN32__ // added to prevent link errors on armv5
1.31 +#include <signal.h>
1.32 +#endif
1.33 +
1.34 +extern TclStubs tclStubs;
1.35 +extern Tcl_NotifierProcs tclOriginalNotifier;
1.36 +
1.37 +/*
1.38 + * This structure is used to keep track of the notifier info for a
1.39 + * a registered file.
1.40 + */
1.41 +
1.42 +typedef struct FileHandler {
1.43 + int fd;
1.44 + int mask; /* Mask of desired events: TCL_READABLE,
1.45 + * etc. */
1.46 + int readyMask; /* Mask of events that have been seen since the
1.47 + * last time file handlers were invoked for
1.48 + * this file. */
1.49 + Tcl_FileProc *proc; /* Procedure to call, in the style of
1.50 + * Tcl_CreateFileHandler. */
1.51 + ClientData clientData; /* Argument to pass to proc. */
1.52 + struct FileHandler *nextPtr;/* Next in list of all files we care about. */
1.53 +} FileHandler;
1.54 +
1.55 +/*
1.56 + * The following structure is what is added to the Tcl event queue when
1.57 + * file handlers are ready to fire.
1.58 + */
1.59 +
1.60 +typedef struct FileHandlerEvent {
1.61 + Tcl_Event header; /* Information that is standard for
1.62 + * all events. */
1.63 + int fd; /* File descriptor that is ready. Used
1.64 + * to find the FileHandler structure for
1.65 + * the file (can't point directly to the
1.66 + * FileHandler structure because it could
1.67 + * go away while the event is queued). */
1.68 +} FileHandlerEvent;
1.69 +
1.70 +/*
1.71 + *
1.72 + * The following structure contains a set of select() masks to track
1.73 + * readable, writable, and exceptional conditions.
1.74 + */
1.75 +
1.76 +typedef struct SelectMasks {
1.77 + fd_set readable;
1.78 + fd_set writable;
1.79 + fd_set exceptional;
1.80 +} SelectMasks;
1.81 +
1.82 +/*
1.83 + * The following static structure contains the state information for the
1.84 + * select based implementation of the Tcl notifier. One of these structures
1.85 + * is created for each thread that is using the notifier.
1.86 + */
1.87 +
1.88 +typedef struct ThreadSpecificData {
1.89 + FileHandler *firstFileHandlerPtr;
1.90 + /* Pointer to head of file handler list. */
1.91 +
1.92 + SelectMasks checkMasks; /* This structure is used to build up the masks
1.93 + * to be used in the next call to select.
1.94 + * Bits are set in response to calls to
1.95 + * Tcl_CreateFileHandler. */
1.96 + SelectMasks readyMasks; /* This array reflects the readable/writable
1.97 + * conditions that were found to exist by the
1.98 + * last call to select. */
1.99 + int numFdBits; /* Number of valid bits in checkMasks
1.100 + * (one more than highest fd for which
1.101 + * Tcl_WatchFile has been called). */
1.102 +#ifdef TCL_THREADS
1.103 + int onList; /* True if it is in this list */
1.104 + unsigned int pollState; /* pollState is used to implement a polling
1.105 + * handshake between each thread and the
1.106 + * notifier thread. Bits defined below. */
1.107 + struct ThreadSpecificData *nextPtr, *prevPtr;
1.108 + /* All threads that are currently waiting on
1.109 + * an event have their ThreadSpecificData
1.110 + * structure on a doubly-linked listed formed
1.111 + * from these pointers. You must hold the
1.112 + * notifierMutex lock before accessing these
1.113 + * fields. */
1.114 + Tcl_Condition waitCV; /* Any other thread alerts a notifier
1.115 + * that an event is ready to be processed
1.116 + * by signaling this condition variable. */
1.117 + int eventReady; /* True if an event is ready to be processed.
1.118 + * Used as condition flag together with
1.119 + * waitCV above. */
1.120 +#endif
1.121 +} ThreadSpecificData;
1.122 +
1.123 +#if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
1.124 +static Tcl_ThreadDataKey dataKey;
1.125 +#endif
1.126 +
1.127 +#ifdef TCL_THREADS
1.128 +/*
1.129 + * The following static indicates the number of threads that have
1.130 + * initialized notifiers.
1.131 + *
1.132 + * You must hold the notifierMutex lock before accessing this variable.
1.133 + */
1.134 +
1.135 +static int notifierCount = 0;
1.136 +
1.137 +/*
1.138 + * The following variable points to the head of a doubly-linked list of
1.139 + * of ThreadSpecificData structures for all threads that are currently
1.140 + * waiting on an event.
1.141 + *
1.142 + * You must hold the notifierMutex lock before accessing this list.
1.143 + */
1.144 +
1.145 +static ThreadSpecificData *waitingListPtr = NULL;
1.146 +
1.147 +/*
1.148 + * The notifier thread spends all its time in select() waiting for a
1.149 + * file descriptor associated with one of the threads on the waitingListPtr
1.150 + * list to do something interesting. But if the contents of the
1.151 + * waitingListPtr list ever changes, we need to wake up and restart
1.152 + * the select() system call. You can wake up the notifier thread by
1.153 + * writing a single byte to the file descriptor defined below. This
1.154 + * file descriptor is the input-end of a pipe and the notifier thread is
1.155 + * listening for data on the output-end of the same pipe. Hence writing
1.156 + * to this file descriptor will cause the select() system call to return
1.157 + * and wake up the notifier thread.
1.158 + *
1.159 + * You must hold the notifierMutex lock before accessing this list.
1.160 + */
1.161 +
1.162 +static int triggerPipe = -1;
1.163 +
1.164 +/*
1.165 + * The notifierMutex locks access to all of the global notifier state.
1.166 + */
1.167 +
1.168 +TCL_DECLARE_MUTEX(notifierMutex)
1.169 +
1.170 +/*
1.171 + * The notifier thread signals the notifierCV when it has finished
1.172 + * initializing the triggerPipe and right before the notifier
1.173 + * thread terminates.
1.174 + */
1.175 +
1.176 +static Tcl_Condition notifierCV;
1.177 +
1.178 +/*
1.179 + * The pollState bits
1.180 + * POLL_WANT is set by each thread before it waits on its condition
1.181 + * variable. It is checked by the notifier before it does
1.182 + * select.
1.183 + * POLL_DONE is set by the notifier if it goes into select after
1.184 + * seeing POLL_WANT. The idea is to ensure it tries a select
1.185 + * with the same bits the initial thread had set.
1.186 + */
1.187 +#define POLL_WANT 0x1
1.188 +#define POLL_DONE 0x2
1.189 +
1.190 +/*
1.191 + * This is the thread ID of the notifier thread that does select.
1.192 + */
1.193 +static Tcl_ThreadId notifierThread;
1.194 +
1.195 +#endif
1.196 +
1.197 +/*
1.198 + * Static routines defined in this file.
1.199 + */
1.200 +
1.201 +#ifdef TCL_THREADS
1.202 +static void NotifierThreadProc _ANSI_ARGS_((ClientData clientData));
1.203 +#endif
1.204 +static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
1.205 + int flags));
1.206 +
1.207 +/*
1.208 + *----------------------------------------------------------------------
1.209 + *
1.210 + * Tcl_InitNotifier --
1.211 + *
1.212 + * Initializes the platform specific notifier state.
1.213 + *
1.214 + * Results:
1.215 + * Returns a handle to the notifier state for this thread..
1.216 + *
1.217 + * Side effects:
1.218 + * None.
1.219 + *
1.220 + *----------------------------------------------------------------------
1.221 + */
1.222 +
1.223 +EXPORT_C ClientData
1.224 +Tcl_InitNotifier()
1.225 +{
1.226 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.227 +
1.228 +#ifdef TCL_THREADS
1.229 + tsdPtr->eventReady = 0;
1.230 +
1.231 + /*
1.232 + * Start the Notifier thread if necessary.
1.233 + */
1.234 +
1.235 + Tcl_MutexLock(¬ifierMutex);
1.236 + if (notifierCount == 0) {
1.237 + if (TclpThreadCreate(¬ifierThread, NotifierThreadProc, NULL,
1.238 + TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
1.239 + panic("Tcl_InitNotifier: unable to start notifier thread");
1.240 + }
1.241 + }
1.242 + notifierCount++;
1.243 +
1.244 + /*
1.245 + * Wait for the notifier pipe to be created.
1.246 + */
1.247 +
1.248 + while (triggerPipe < 0) {
1.249 + Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL);
1.250 + }
1.251 +
1.252 + Tcl_MutexUnlock(¬ifierMutex);
1.253 +#endif
1.254 + return (ClientData) tsdPtr;
1.255 +}
1.256 +
1.257 +/*
1.258 + *----------------------------------------------------------------------
1.259 + *
1.260 + * Tcl_FinalizeNotifier --
1.261 + *
1.262 + * This function is called to cleanup the notifier state before
1.263 + * a thread is terminated.
1.264 + *
1.265 + * Results:
1.266 + * None.
1.267 + *
1.268 + * Side effects:
1.269 + * May terminate the background notifier thread if this is the
1.270 + * last notifier instance.
1.271 + *
1.272 + *----------------------------------------------------------------------
1.273 + */
1.274 +
1.275 +EXPORT_C void
1.276 +Tcl_FinalizeNotifier(clientData)
1.277 + ClientData clientData; /* Not used. */
1.278 +{
1.279 +#ifdef TCL_THREADS
1.280 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.281 +
1.282 + Tcl_MutexLock(¬ifierMutex);
1.283 + notifierCount--;
1.284 +
1.285 + /*
1.286 + * If this is the last thread to use the notifier, close the notifier
1.287 + * pipe and wait for the background thread to terminate.
1.288 + */
1.289 +
1.290 + if (notifierCount == 0) {
1.291 + int result;
1.292 + if (triggerPipe < 0) {
1.293 + panic("Tcl_FinalizeNotifier: notifier pipe not initialized");
1.294 + }
1.295 +
1.296 + /*
1.297 + * Send "q" message to the notifier thread so that it will
1.298 + * terminate. The notifier will return from its call to select()
1.299 + * and notice that a "q" message has arrived, it will then close
1.300 + * its side of the pipe and terminate its thread. Note the we can
1.301 + * not just close the pipe and check for EOF in the notifier
1.302 + * thread because if a background child process was created with
1.303 + * exec, select() would not register the EOF on the pipe until the
1.304 + * child processes had terminated. [Bug: 4139] [Bug: 1222872]
1.305 + */
1.306 +
1.307 + write(triggerPipe, "q", 1);
1.308 + close(triggerPipe);
1.309 + while(triggerPipe >= 0) {
1.310 + Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL);
1.311 + }
1.312 + result = Tcl_JoinThread(notifierThread, NULL);
1.313 + if (result) {
1.314 + Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread");
1.315 + }
1.316 + }
1.317 +
1.318 + /*
1.319 + * Clean up any synchronization objects in the thread local storage.
1.320 + */
1.321 +
1.322 + Tcl_ConditionFinalize(&(tsdPtr->waitCV));
1.323 +
1.324 + Tcl_MutexUnlock(¬ifierMutex);
1.325 +#endif
1.326 +}
1.327 +
1.328 +/*
1.329 + *----------------------------------------------------------------------
1.330 + *
1.331 + * Tcl_AlertNotifier --
1.332 + *
1.333 + * Wake up the specified notifier from any thread. This routine
1.334 + * is called by the platform independent notifier code whenever
1.335 + * the Tcl_ThreadAlert routine is called. This routine is
1.336 + * guaranteed not to be called on a given notifier after
1.337 + * Tcl_FinalizeNotifier is called for that notifier.
1.338 + *
1.339 + * Results:
1.340 + * None.
1.341 + *
1.342 + * Side effects:
1.343 + * Signals the notifier condition variable for the specified
1.344 + * notifier.
1.345 + *
1.346 + *----------------------------------------------------------------------
1.347 + */
1.348 +
1.349 +EXPORT_C void
1.350 +Tcl_AlertNotifier(clientData)
1.351 + ClientData clientData;
1.352 +{
1.353 +#ifdef TCL_THREADS
1.354 + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
1.355 + Tcl_MutexLock(¬ifierMutex);
1.356 + tsdPtr->eventReady = 1;
1.357 + Tcl_ConditionNotify(&tsdPtr->waitCV);
1.358 + Tcl_MutexUnlock(¬ifierMutex);
1.359 +#endif
1.360 +}
1.361 +
1.362 +/*
1.363 + *----------------------------------------------------------------------
1.364 + *
1.365 + * Tcl_SetTimer --
1.366 + *
1.367 + * This procedure sets the current notifier timer value. This
1.368 + * interface is not implemented in this notifier because we are
1.369 + * always running inside of Tcl_DoOneEvent.
1.370 + *
1.371 + * Results:
1.372 + * None.
1.373 + *
1.374 + * Side effects:
1.375 + * None.
1.376 + *
1.377 + *----------------------------------------------------------------------
1.378 + */
1.379 +
1.380 +EXPORT_C void
1.381 +Tcl_SetTimer(timePtr)
1.382 + Tcl_Time *timePtr; /* Timeout value, may be NULL. */
1.383 +{
1.384 + /*
1.385 + * The interval timer doesn't do anything in this implementation,
1.386 + * because the only event loop is via Tcl_DoOneEvent, which passes
1.387 + * timeout values to Tcl_WaitForEvent.
1.388 + */
1.389 +
1.390 + if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
1.391 + tclStubs.tcl_SetTimer(timePtr);
1.392 + }
1.393 +}
1.394 +
1.395 +/*
1.396 + *----------------------------------------------------------------------
1.397 + *
1.398 + * Tcl_ServiceModeHook --
1.399 + *
1.400 + * This function is invoked whenever the service mode changes.
1.401 + *
1.402 + * Results:
1.403 + * None.
1.404 + *
1.405 + * Side effects:
1.406 + * None.
1.407 + *
1.408 + *----------------------------------------------------------------------
1.409 + */
1.410 +
1.411 +EXPORT_C void
1.412 +Tcl_ServiceModeHook(mode)
1.413 + int mode; /* Either TCL_SERVICE_ALL, or
1.414 + * TCL_SERVICE_NONE. */
1.415 +{
1.416 +}
1.417 +
1.418 +/*
1.419 + *----------------------------------------------------------------------
1.420 + *
1.421 + * Tcl_CreateFileHandler --
1.422 + *
1.423 + * This procedure registers a file handler with the select notifier.
1.424 + *
1.425 + * Results:
1.426 + * None.
1.427 + *
1.428 + * Side effects:
1.429 + * Creates a new file handler structure.
1.430 + *
1.431 + *----------------------------------------------------------------------
1.432 + */
1.433 +
1.434 +EXPORT_C void
1.435 +Tcl_CreateFileHandler(fd, mask, proc, clientData)
1.436 + int fd; /* Handle of stream to watch. */
1.437 + int mask; /* OR'ed combination of TCL_READABLE,
1.438 + * TCL_WRITABLE, and TCL_EXCEPTION:
1.439 + * indicates conditions under which
1.440 + * proc should be called. */
1.441 + Tcl_FileProc *proc; /* Procedure to call for each
1.442 + * selected event. */
1.443 + ClientData clientData; /* Arbitrary data to pass to proc. */
1.444 +{
1.445 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.446 + FileHandler *filePtr;
1.447 +
1.448 + if (tclStubs.tcl_CreateFileHandler != tclOriginalNotifier.createFileHandlerProc) {
1.449 + tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData);
1.450 + return;
1.451 + }
1.452 +
1.453 + for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
1.454 + filePtr = filePtr->nextPtr) {
1.455 + if (filePtr->fd == fd) {
1.456 + break;
1.457 + }
1.458 + }
1.459 + if (filePtr == NULL) {
1.460 + filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
1.461 + filePtr->fd = fd;
1.462 + filePtr->readyMask = 0;
1.463 + filePtr->nextPtr = tsdPtr->firstFileHandlerPtr;
1.464 + tsdPtr->firstFileHandlerPtr = filePtr;
1.465 + }
1.466 + filePtr->proc = proc;
1.467 + filePtr->clientData = clientData;
1.468 + filePtr->mask = mask;
1.469 +
1.470 + /*
1.471 + * Update the check masks for this file.
1.472 + */
1.473 +
1.474 + if ( mask & TCL_READABLE ) {
1.475 + FD_SET( fd, &(tsdPtr->checkMasks.readable) );
1.476 + } else {
1.477 + FD_CLR( fd, &(tsdPtr->checkMasks.readable) );
1.478 + }
1.479 + if ( mask & TCL_WRITABLE ) {
1.480 + FD_SET( fd, &(tsdPtr->checkMasks.writable) );
1.481 + } else {
1.482 + FD_CLR( fd, &(tsdPtr->checkMasks.writable) );
1.483 + }
1.484 + if ( mask & TCL_EXCEPTION ) {
1.485 + FD_SET( fd, &(tsdPtr->checkMasks.exceptional) );
1.486 + } else {
1.487 + FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) );
1.488 + }
1.489 + if (tsdPtr->numFdBits <= fd) {
1.490 + tsdPtr->numFdBits = fd+1;
1.491 + }
1.492 +}
1.493 +
1.494 +/*
1.495 + *----------------------------------------------------------------------
1.496 + *
1.497 + * Tcl_DeleteFileHandler --
1.498 + *
1.499 + * Cancel a previously-arranged callback arrangement for
1.500 + * a file.
1.501 + *
1.502 + * Results:
1.503 + * None.
1.504 + *
1.505 + * Side effects:
1.506 + * If a callback was previously registered on file, remove it.
1.507 + *
1.508 + *----------------------------------------------------------------------
1.509 + */
1.510 +
1.511 +EXPORT_C void
1.512 +Tcl_DeleteFileHandler(fd)
1.513 + int fd; /* Stream id for which to remove callback procedure. */
1.514 +{
1.515 + FileHandler *filePtr, *prevPtr;
1.516 + int i;
1.517 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.518 +
1.519 + if (tclStubs.tcl_DeleteFileHandler != tclOriginalNotifier.deleteFileHandlerProc) {
1.520 + tclStubs.tcl_DeleteFileHandler(fd);
1.521 + return;
1.522 + }
1.523 +
1.524 + /*
1.525 + * Find the entry for the given file (and return if there isn't one).
1.526 + */
1.527 +
1.528 + for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ;
1.529 + prevPtr = filePtr, filePtr = filePtr->nextPtr) {
1.530 + if (filePtr == NULL) {
1.531 + return;
1.532 + }
1.533 + if (filePtr->fd == fd) {
1.534 + break;
1.535 + }
1.536 + }
1.537 +
1.538 + /*
1.539 + * Update the check masks for this file.
1.540 + */
1.541 +
1.542 + if (filePtr->mask & TCL_READABLE) {
1.543 + FD_CLR( fd, &(tsdPtr->checkMasks.readable) );
1.544 + }
1.545 + if (filePtr->mask & TCL_WRITABLE) {
1.546 + FD_CLR( fd, &(tsdPtr->checkMasks.writable) );
1.547 + }
1.548 + if (filePtr->mask & TCL_EXCEPTION) {
1.549 + FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) );
1.550 + }
1.551 +
1.552 + /*
1.553 + * Find current max fd.
1.554 + */
1.555 +
1.556 + if (fd+1 == tsdPtr->numFdBits) {
1.557 + tsdPtr->numFdBits = 0;
1.558 + for (i = fd-1; i >= 0; i--) {
1.559 + if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) )
1.560 + || FD_ISSET( i, &(tsdPtr->checkMasks.writable) )
1.561 + || FD_ISSET( i, &(tsdPtr->checkMasks.exceptional ) ) ) {
1.562 + tsdPtr->numFdBits = i+1;
1.563 + break;
1.564 + }
1.565 + }
1.566 + }
1.567 +
1.568 + /*
1.569 + * Clean up information in the callback record.
1.570 + */
1.571 +
1.572 + if (prevPtr == NULL) {
1.573 + tsdPtr->firstFileHandlerPtr = filePtr->nextPtr;
1.574 + } else {
1.575 + prevPtr->nextPtr = filePtr->nextPtr;
1.576 + }
1.577 + ckfree((char *) filePtr);
1.578 +}
1.579 +
1.580 +/*
1.581 + *----------------------------------------------------------------------
1.582 + *
1.583 + * FileHandlerEventProc --
1.584 + *
1.585 + * This procedure is called by Tcl_ServiceEvent when a file event
1.586 + * reaches the front of the event queue. This procedure is
1.587 + * responsible for actually handling the event by invoking the
1.588 + * callback for the file handler.
1.589 + *
1.590 + * Results:
1.591 + * Returns 1 if the event was handled, meaning it should be removed
1.592 + * from the queue. Returns 0 if the event was not handled, meaning
1.593 + * it should stay on the queue. The only time the event isn't
1.594 + * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1.595 + *
1.596 + * Side effects:
1.597 + * Whatever the file handler's callback procedure does.
1.598 + *
1.599 + *----------------------------------------------------------------------
1.600 + */
1.601 +
1.602 +static int
1.603 +FileHandlerEventProc(evPtr, flags)
1.604 + Tcl_Event *evPtr; /* Event to service. */
1.605 + int flags; /* Flags that indicate what events to
1.606 + * handle, such as TCL_FILE_EVENTS. */
1.607 +{
1.608 + int mask;
1.609 + FileHandler *filePtr;
1.610 + FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
1.611 + ThreadSpecificData *tsdPtr;
1.612 +
1.613 + if (!(flags & TCL_FILE_EVENTS)) {
1.614 + return 0;
1.615 + }
1.616 +
1.617 + /*
1.618 + * Search through the file handlers to find the one whose handle matches
1.619 + * the event. We do this rather than keeping a pointer to the file
1.620 + * handler directly in the event, so that the handler can be deleted
1.621 + * while the event is queued without leaving a dangling pointer.
1.622 + */
1.623 +
1.624 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.625 + for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
1.626 + filePtr = filePtr->nextPtr) {
1.627 + if (filePtr->fd != fileEvPtr->fd) {
1.628 + continue;
1.629 + }
1.630 +
1.631 + /*
1.632 + * The code is tricky for two reasons:
1.633 + * 1. The file handler's desired events could have changed
1.634 + * since the time when the event was queued, so AND the
1.635 + * ready mask with the desired mask.
1.636 + * 2. The file could have been closed and re-opened since
1.637 + * the time when the event was queued. This is why the
1.638 + * ready mask is stored in the file handler rather than
1.639 + * the queued event: it will be zeroed when a new
1.640 + * file handler is created for the newly opened file.
1.641 + */
1.642 +
1.643 + mask = filePtr->readyMask & filePtr->mask;
1.644 + filePtr->readyMask = 0;
1.645 + if (mask != 0) {
1.646 + (*filePtr->proc)(filePtr->clientData, mask);
1.647 + }
1.648 + break;
1.649 + }
1.650 + return 1;
1.651 +}
1.652 +
1.653 +/*
1.654 + *----------------------------------------------------------------------
1.655 + *
1.656 + * Tcl_WaitForEvent --
1.657 + *
1.658 + * This function is called by Tcl_DoOneEvent to wait for new
1.659 + * events on the message queue. If the block time is 0, then
1.660 + * Tcl_WaitForEvent just polls without blocking.
1.661 + *
1.662 + * Results:
1.663 + * Returns -1 if the select would block forever, otherwise
1.664 + * returns 0.
1.665 + *
1.666 + * Side effects:
1.667 + * Queues file events that are detected by the select.
1.668 + *
1.669 + *----------------------------------------------------------------------
1.670 + */
1.671 +
1.672 +EXPORT_C int
1.673 +Tcl_WaitForEvent(timePtr)
1.674 + Tcl_Time *timePtr; /* Maximum block time, or NULL. */
1.675 +{
1.676 + FileHandler *filePtr;
1.677 + FileHandlerEvent *fileEvPtr;
1.678 + int mask;
1.679 +#ifdef TCL_THREADS
1.680 + int waitForFiles;
1.681 +#else
1.682 + /* Impl. notes: timeout & timeoutPtr are used if, and only if
1.683 + * threads are not enabled. They are the arguments for the regular
1.684 + * select() used when the core is not thread-enabled. */
1.685 +
1.686 + struct timeval timeout, *timeoutPtr;
1.687 + int numFound;
1.688 +#endif
1.689 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.690 +
1.691 + if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
1.692 + return tclStubs.tcl_WaitForEvent(timePtr);
1.693 + }
1.694 +
1.695 +#ifndef TCL_THREADS
1.696 + /*
1.697 + * Set up the timeout structure. Note that if there are no events to
1.698 + * check for, we return with a negative result rather than blocking
1.699 + * forever.
1.700 + */
1.701 +
1.702 + if (timePtr) {
1.703 + timeout.tv_sec = timePtr->sec;
1.704 + timeout.tv_usec = timePtr->usec;
1.705 + timeoutPtr = &timeout;
1.706 + } else if (tsdPtr->numFdBits == 0) {
1.707 + /*
1.708 + * If there are no threads, no timeout, and no fds registered,
1.709 + * then there are no events possible and we must avoid deadlock.
1.710 + * Note that this is not entirely correct because there might
1.711 + * be a signal that could interrupt the select call, but we
1.712 + * don't handle that case if we aren't using threads.
1.713 + */
1.714 +
1.715 + return -1;
1.716 + } else {
1.717 + timeoutPtr = NULL;
1.718 + }
1.719 +#endif
1.720 +
1.721 +#ifdef TCL_THREADS
1.722 + /*
1.723 + * Place this thread on the list of interested threads, signal the
1.724 + * notifier thread, and wait for a response or a timeout.
1.725 + */
1.726 +
1.727 + Tcl_MutexLock(¬ifierMutex);
1.728 +
1.729 + waitForFiles = (tsdPtr->numFdBits > 0);
1.730 + if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0
1.731 +#if defined(__APPLE__) && defined(__LP64__)
1.732 + /*
1.733 + * On 64-bit Darwin, pthread_cond_timedwait() appears to have a bug
1.734 + * that causes it to wait forever when passed an absolute time which
1.735 + * has already been exceeded by the system time; as a workaround,
1.736 + * when given a very brief timeout, just do a poll. [Bug 1457797]
1.737 + */
1.738 + || timePtr->usec < 10
1.739 +#endif
1.740 + )) {
1.741 + /*
1.742 + * Cannot emulate a polling select with a polling condition variable.
1.743 + * Instead, pretend to wait for files and tell the notifier
1.744 + * thread what we are doing. The notifier thread makes sure
1.745 + * it goes through select with its select mask in the same state
1.746 + * as ours currently is. We block until that happens.
1.747 + */
1.748 +
1.749 + waitForFiles = 1;
1.750 + tsdPtr->pollState = POLL_WANT;
1.751 + timePtr = NULL;
1.752 + } else {
1.753 + tsdPtr->pollState = 0;
1.754 + }
1.755 +
1.756 + if (waitForFiles) {
1.757 + /*
1.758 + * Add the ThreadSpecificData structure of this thread to the list
1.759 + * of ThreadSpecificData structures of all threads that are waiting
1.760 + * on file events.
1.761 + */
1.762 +
1.763 +
1.764 + tsdPtr->nextPtr = waitingListPtr;
1.765 + if (waitingListPtr) {
1.766 + waitingListPtr->prevPtr = tsdPtr;
1.767 + }
1.768 + tsdPtr->prevPtr = 0;
1.769 + waitingListPtr = tsdPtr;
1.770 + tsdPtr->onList = 1;
1.771 +
1.772 + write(triggerPipe, "", 1);
1.773 + }
1.774 +
1.775 + FD_ZERO( &(tsdPtr->readyMasks.readable) );
1.776 + FD_ZERO( &(tsdPtr->readyMasks.writable) );
1.777 + FD_ZERO( &(tsdPtr->readyMasks.exceptional) );
1.778 +
1.779 + if (!tsdPtr->eventReady) {
1.780 + Tcl_ConditionWait(&tsdPtr->waitCV, ¬ifierMutex, timePtr);
1.781 + }
1.782 + tsdPtr->eventReady = 0;
1.783 +
1.784 + if (waitForFiles && tsdPtr->onList) {
1.785 + /*
1.786 + * Remove the ThreadSpecificData structure of this thread from the
1.787 + * waiting list. Alert the notifier thread to recompute its select
1.788 + * masks - skipping this caused a hang when trying to close a pipe
1.789 + * which the notifier thread was still doing a select on.
1.790 + */
1.791 +
1.792 + if (tsdPtr->prevPtr) {
1.793 + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
1.794 + } else {
1.795 + waitingListPtr = tsdPtr->nextPtr;
1.796 + }
1.797 + if (tsdPtr->nextPtr) {
1.798 + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
1.799 + }
1.800 + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
1.801 + tsdPtr->onList = 0;
1.802 + write(triggerPipe, "", 1);
1.803 + }
1.804 +
1.805 +
1.806 +#else
1.807 + tsdPtr->readyMasks = tsdPtr->checkMasks;
1.808 + numFound = select( tsdPtr->numFdBits,
1.809 + &(tsdPtr->readyMasks.readable),
1.810 + &(tsdPtr->readyMasks.writable),
1.811 + &(tsdPtr->readyMasks.exceptional),
1.812 + timeoutPtr );
1.813 +
1.814 + /*
1.815 + * Some systems don't clear the masks after an error, so
1.816 + * we have to do it here.
1.817 + */
1.818 +
1.819 + if (numFound == -1) {
1.820 + FD_ZERO( &(tsdPtr->readyMasks.readable ) );
1.821 + FD_ZERO( &(tsdPtr->readyMasks.writable ) );
1.822 + FD_ZERO( &(tsdPtr->readyMasks.exceptional ) );
1.823 + }
1.824 +#endif
1.825 +
1.826 + /*
1.827 + * Queue all detected file events before returning.
1.828 + */
1.829 +
1.830 + for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL);
1.831 + filePtr = filePtr->nextPtr) {
1.832 +
1.833 + mask = 0;
1.834 + if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.readable) ) ) {
1.835 + mask |= TCL_READABLE;
1.836 + }
1.837 + if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.writable) ) ) {
1.838 + mask |= TCL_WRITABLE;
1.839 + }
1.840 + if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.exceptional) ) ) {
1.841 + mask |= TCL_EXCEPTION;
1.842 + }
1.843 +
1.844 + if (!mask) {
1.845 + continue;
1.846 + }
1.847 +
1.848 + /*
1.849 + * Don't bother to queue an event if the mask was previously
1.850 + * non-zero since an event must still be on the queue.
1.851 + */
1.852 +
1.853 + if (filePtr->readyMask == 0) {
1.854 + fileEvPtr = (FileHandlerEvent *) ckalloc(
1.855 + sizeof(FileHandlerEvent));
1.856 + fileEvPtr->header.proc = FileHandlerEventProc;
1.857 + fileEvPtr->fd = filePtr->fd;
1.858 + Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
1.859 + }
1.860 + filePtr->readyMask = mask;
1.861 + }
1.862 +#ifdef TCL_THREADS
1.863 + Tcl_MutexUnlock(¬ifierMutex);
1.864 +#endif
1.865 + return 0;
1.866 +}
1.867 +
1.868 +#ifdef TCL_THREADS
1.869 +/*
1.870 + *----------------------------------------------------------------------
1.871 + *
1.872 + * NotifierThreadProc --
1.873 + *
1.874 + * This routine is the initial (and only) function executed by the
1.875 + * special notifier thread. Its job is to wait for file descriptors
1.876 + * to become readable or writable or to have an exception condition
1.877 + * and then to notify other threads who are interested in this
1.878 + * information by signalling a condition variable. Other threads
1.879 + * can signal this notifier thread of a change in their interests
1.880 + * by writing a single byte to a special pipe that the notifier
1.881 + * thread is monitoring.
1.882 + *
1.883 + * Result:
1.884 + * None. Once started, this routine never exits. It dies with
1.885 + * the overall process.
1.886 + *
1.887 + * Side effects:
1.888 + * The trigger pipe used to signal the notifier thread is created
1.889 + * when the notifier thread first starts.
1.890 + *
1.891 + *----------------------------------------------------------------------
1.892 + */
1.893 +
1.894 +static void
1.895 +NotifierThreadProc(clientData)
1.896 + ClientData clientData; /* Not used. */
1.897 +{
1.898 + ThreadSpecificData *tsdPtr;
1.899 + fd_set readableMask;
1.900 + fd_set writableMask;
1.901 + fd_set exceptionalMask;
1.902 + int fds[2];
1.903 + int i, status, numFdBits = 0, receivePipe;
1.904 + long found;
1.905 + struct timeval poll = {0., 0.}, *timePtr;
1.906 + char buf[2];
1.907 +
1.908 + if (pipe(fds) != 0) {
1.909 + panic("NotifierThreadProc: could not create trigger pipe.");
1.910 + }
1.911 +
1.912 + receivePipe = fds[0];
1.913 +
1.914 +#ifndef USE_FIONBIO
1.915 + status = fcntl(receivePipe, F_GETFL);
1.916 + status |= O_NONBLOCK;
1.917 + if (fcntl(receivePipe, F_SETFL, status) < 0) {
1.918 + panic("NotifierThreadProc: could not make receive pipe non blocking.");
1.919 + }
1.920 + status = fcntl(fds[1], F_GETFL);
1.921 + status |= O_NONBLOCK;
1.922 + if (fcntl(fds[1], F_SETFL, status) < 0) {
1.923 + panic("NotifierThreadProc: could not make trigger pipe non blocking.");
1.924 + }
1.925 +#else
1.926 + if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) {
1.927 + panic("NotifierThreadProc: could not make receive pipe non blocking.");
1.928 + }
1.929 + if (ioctl(fds[1], (int) FIONBIO, &status) < 0) {
1.930 + panic("NotifierThreadProc: could not make trigger pipe non blocking.");
1.931 + }
1.932 +#endif
1.933 +
1.934 + /*
1.935 + * Install the write end of the pipe into the global variable.
1.936 + */
1.937 +
1.938 + Tcl_MutexLock(¬ifierMutex);
1.939 + triggerPipe = fds[1];
1.940 +
1.941 + /*
1.942 + * Signal any threads that are waiting.
1.943 + */
1.944 +
1.945 + Tcl_ConditionNotify(¬ifierCV);
1.946 + Tcl_MutexUnlock(¬ifierMutex);
1.947 +
1.948 + /*
1.949 + * Look for file events and report them to interested threads.
1.950 + */
1.951 +
1.952 + while (1) {
1.953 +
1.954 + FD_ZERO( &readableMask );
1.955 + FD_ZERO( &writableMask );
1.956 + FD_ZERO( &exceptionalMask );
1.957 +
1.958 + /*
1.959 + * Compute the logical OR of the select masks from all the
1.960 + * waiting notifiers.
1.961 + */
1.962 +
1.963 + Tcl_MutexLock(¬ifierMutex);
1.964 + timePtr = NULL;
1.965 + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1.966 + for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) {
1.967 + if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) ) ) {
1.968 + FD_SET( i, &readableMask );
1.969 + }
1.970 + if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) ) ) {
1.971 + FD_SET( i, &writableMask );
1.972 + }
1.973 + if ( FD_ISSET( i, &(tsdPtr->checkMasks.exceptional) ) ) {
1.974 + FD_SET( i, &exceptionalMask );
1.975 + }
1.976 + }
1.977 + if ( tsdPtr->numFdBits > numFdBits ) {
1.978 + numFdBits = tsdPtr->numFdBits;
1.979 + }
1.980 + if (tsdPtr->pollState & POLL_WANT) {
1.981 + /*
1.982 + * Here we make sure we go through select() with the same
1.983 + * mask bits that were present when the thread tried to poll.
1.984 + */
1.985 +
1.986 + tsdPtr->pollState |= POLL_DONE;
1.987 + timePtr = &poll;
1.988 + }
1.989 + }
1.990 + Tcl_MutexUnlock(¬ifierMutex);
1.991 +
1.992 + /*
1.993 + * Set up the select mask to include the receive pipe.
1.994 + */
1.995 +
1.996 + if ( receivePipe >= numFdBits ) {
1.997 + numFdBits = receivePipe + 1;
1.998 + }
1.999 + FD_SET( receivePipe, &readableMask );
1.1000 +
1.1001 + if ( select( numFdBits, &readableMask, &writableMask,
1.1002 + &exceptionalMask, timePtr) == -1 ) {
1.1003 + /*
1.1004 + * Try again immediately on an error.
1.1005 + */
1.1006 +
1.1007 + continue;
1.1008 + }
1.1009 +
1.1010 + /*
1.1011 + * Alert any threads that are waiting on a ready file descriptor.
1.1012 + */
1.1013 +
1.1014 + Tcl_MutexLock(¬ifierMutex);
1.1015 + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1.1016 + found = 0;
1.1017 +
1.1018 + for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) {
1.1019 + if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) )
1.1020 + && FD_ISSET( i, &readableMask ) ) {
1.1021 + FD_SET( i, &(tsdPtr->readyMasks.readable) );
1.1022 + found = 1;
1.1023 + }
1.1024 + if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) )
1.1025 + && FD_ISSET( i, &writableMask ) ) {
1.1026 + FD_SET( i, &(tsdPtr->readyMasks.writable) );
1.1027 + found = 1;
1.1028 + }
1.1029 + if ( FD_ISSET( i, &(tsdPtr->checkMasks.exceptional) )
1.1030 + && FD_ISSET( i, &exceptionalMask ) ) {
1.1031 + FD_SET( i, &(tsdPtr->readyMasks.exceptional) );
1.1032 + found = 1;
1.1033 + }
1.1034 + }
1.1035 +
1.1036 + if (found || (tsdPtr->pollState & POLL_DONE)) {
1.1037 + tsdPtr->eventReady = 1;
1.1038 + if (tsdPtr->onList) {
1.1039 + /*
1.1040 + * Remove the ThreadSpecificData structure of this
1.1041 + * thread from the waiting list. This prevents us from
1.1042 + * continuously spining on select until the other
1.1043 + * threads runs and services the file event.
1.1044 + */
1.1045 +
1.1046 + if (tsdPtr->prevPtr) {
1.1047 + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
1.1048 + } else {
1.1049 + waitingListPtr = tsdPtr->nextPtr;
1.1050 + }
1.1051 + if (tsdPtr->nextPtr) {
1.1052 + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
1.1053 + }
1.1054 + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
1.1055 + tsdPtr->onList = 0;
1.1056 + tsdPtr->pollState = 0;
1.1057 + }
1.1058 + Tcl_ConditionNotify(&tsdPtr->waitCV);
1.1059 + }
1.1060 + }
1.1061 + Tcl_MutexUnlock(¬ifierMutex);
1.1062 +
1.1063 + /*
1.1064 + * Consume the next byte from the notifier pipe if the pipe was
1.1065 + * readable. Note that there may be multiple bytes pending, but
1.1066 + * to avoid a race condition we only read one at a time.
1.1067 + */
1.1068 +
1.1069 + if ( FD_ISSET( receivePipe, &readableMask ) ) {
1.1070 + i = read(receivePipe, buf, 1);
1.1071 +
1.1072 + if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) {
1.1073 + /*
1.1074 + * Someone closed the write end of the pipe or sent us a
1.1075 + * Quit message [Bug: 4139] and then closed the write end
1.1076 + * of the pipe so we need to shut down the notifier thread.
1.1077 + */
1.1078 +
1.1079 + break;
1.1080 + }
1.1081 + }
1.1082 + }
1.1083 +
1.1084 + /*
1.1085 + * Clean up the read end of the pipe and signal any threads waiting on
1.1086 + * termination of the notifier thread.
1.1087 + */
1.1088 +
1.1089 + close(receivePipe);
1.1090 + Tcl_MutexLock(¬ifierMutex);
1.1091 + triggerPipe = -1;
1.1092 + Tcl_ConditionNotify(¬ifierCV);
1.1093 + Tcl_MutexUnlock(¬ifierMutex);
1.1094 +
1.1095 + TclpThreadExit (0);
1.1096 +}
1.1097 +#endif
1.1098 +
1.1099 +#endif /* HAVE_COREFOUNDATION */