tclAsync.c

Go to the documentation of this file.
00001 /*
00002  * tclAsync.c --
00003  *
00004  *      This file provides low-level support needed to invoke signal handlers
00005  *      in a safe way. The code here doesn't actually handle signals, though.
00006  *      This code is based on proposals made by Mark Diekhans and Don Libes.
00007  *
00008  * Copyright (c) 1993 The Regents of the University of California.
00009  * Copyright (c) 1994 Sun Microsystems, Inc.
00010  *
00011  * See the file "license.terms" for information on usage and redistribution of
00012  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00013  *
00014  * RCS: @(#) $Id: tclAsync.c,v 1.13 2007/12/13 15:23:14 dgp Exp $
00015  */
00016 
00017 #include "tclInt.h"
00018 
00019 /* Forward declaration */
00020 struct ThreadSpecificData;
00021 
00022 /*
00023  * One of the following structures exists for each asynchronous handler:
00024  */
00025 
00026 typedef struct AsyncHandler {
00027     int ready;                  /* Non-zero means this handler should be
00028                                  * invoked in the next call to
00029                                  * Tcl_AsyncInvoke. */
00030     struct AsyncHandler *nextPtr;
00031                                 /* Next in list of all handlers for the
00032                                  * process. */
00033     Tcl_AsyncProc *proc;        /* Procedure to call when handler is
00034                                  * invoked. */
00035     ClientData clientData;      /* Value to pass to handler when it is
00036                                  * invoked. */
00037     struct ThreadSpecificData *originTsd;
00038                                 /* Used in Tcl_AsyncMark to modify thread-
00039                                  * specific data from outside the thread it is
00040                                  * associated to. */
00041     Tcl_ThreadId originThrdId;  /* Origin thread where this token was created
00042                                  * and where it will be yielded. */
00043 } AsyncHandler;
00044 
00045 typedef struct ThreadSpecificData {
00046     /*
00047      * The variables below maintain a list of all existing handlers specific
00048      * to the calling thread.
00049      */
00050     AsyncHandler *firstHandler; /* First handler defined for process, or NULL
00051                                  * if none. */
00052     AsyncHandler *lastHandler;  /* Last handler or NULL. */
00053     int asyncReady;             /* This is set to 1 whenever a handler becomes
00054                                  * ready and it is cleared to zero whenever
00055                                  * Tcl_AsyncInvoke is called. It can be
00056                                  * checked elsewhere in the application by
00057                                  * calling Tcl_AsyncReady to see if
00058                                  * Tcl_AsyncInvoke should be invoked. */
00059     int asyncActive;            /* Indicates whether Tcl_AsyncInvoke is
00060                                  * currently working. If so then we won't set
00061                                  * asyncReady again until Tcl_AsyncInvoke
00062                                  * returns. */
00063     Tcl_Mutex asyncMutex;       /* Thread-specific AsyncHandler linked-list
00064                                  * lock */
00065 } ThreadSpecificData;
00066 static Tcl_ThreadDataKey dataKey;
00067 
00068 /*
00069  *----------------------------------------------------------------------
00070  *
00071  * TclFinalizeAsync --
00072  *
00073  *      Finalizes the mutex in the thread local data structure for the async
00074  *      subsystem.
00075  *
00076  * Results:
00077  *      None.
00078  *
00079  * Side effects:
00080  *      Forgets knowledge of the mutex should it have been created.
00081  *
00082  *----------------------------------------------------------------------
00083  */
00084 
00085 void
00086 TclFinalizeAsync(void)
00087 {
00088     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00089 
00090     if (tsdPtr->asyncMutex != NULL) {
00091         Tcl_MutexFinalize(&tsdPtr->asyncMutex);
00092     }
00093 }
00094 
00095 /*
00096  *----------------------------------------------------------------------
00097  *
00098  * Tcl_AsyncCreate --
00099  *
00100  *      This procedure creates the data structures for an asynchronous
00101  *      handler, so that no memory has to be allocated when the handler is
00102  *      activated.
00103  *
00104  * Results:
00105  *      The return value is a token for the handler, which can be used to
00106  *      activate it later on.
00107  *
00108  * Side effects:
00109  *      Information about the handler is recorded.
00110  *
00111  *----------------------------------------------------------------------
00112  */
00113 
00114 Tcl_AsyncHandler
00115 Tcl_AsyncCreate(
00116     Tcl_AsyncProc *proc,        /* Procedure to call when handler is
00117                                  * invoked. */
00118     ClientData clientData)      /* Argument to pass to handler. */
00119 {
00120     AsyncHandler *asyncPtr;
00121     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00122 
00123     asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
00124     asyncPtr->ready = 0;
00125     asyncPtr->nextPtr = NULL;
00126     asyncPtr->proc = proc;
00127     asyncPtr->clientData = clientData;
00128     asyncPtr->originTsd = tsdPtr;
00129     asyncPtr->originThrdId = Tcl_GetCurrentThread();
00130 
00131     Tcl_MutexLock(&tsdPtr->asyncMutex);
00132     if (tsdPtr->firstHandler == NULL) {
00133         tsdPtr->firstHandler = asyncPtr;
00134     } else {
00135         tsdPtr->lastHandler->nextPtr = asyncPtr;
00136     }
00137     tsdPtr->lastHandler = asyncPtr;
00138     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
00139     return (Tcl_AsyncHandler) asyncPtr;
00140 }
00141 
00142 /*
00143  *----------------------------------------------------------------------
00144  *
00145  * Tcl_AsyncMark --
00146  *
00147  *      This procedure is called to request that an asynchronous handler be
00148  *      invoked as soon as possible. It's typically called from an interrupt
00149  *      handler, where it isn't safe to do anything that depends on or
00150  *      modifies application state.
00151  *
00152  * Results:
00153  *      None.
00154  *
00155  * Side effects:
00156  *      The handler gets marked for invocation later.
00157  *
00158  *----------------------------------------------------------------------
00159  */
00160 
00161 void
00162 Tcl_AsyncMark(
00163     Tcl_AsyncHandler async)             /* Token for handler. */
00164 {
00165     AsyncHandler *token = (AsyncHandler *) async;
00166 
00167     Tcl_MutexLock(&token->originTsd->asyncMutex);
00168     token->ready = 1;
00169     if (!token->originTsd->asyncActive) {
00170         token->originTsd->asyncReady = 1;
00171         Tcl_ThreadAlert(token->originThrdId);
00172     }
00173     Tcl_MutexUnlock(&token->originTsd->asyncMutex);
00174 }
00175 
00176 /*
00177  *----------------------------------------------------------------------
00178  *
00179  * Tcl_AsyncInvoke --
00180  *
00181  *      This procedure is called at a "safe" time at background level to
00182  *      invoke any active asynchronous handlers.
00183  *
00184  * Results:
00185  *      The return value is a normal Tcl result, which is intended to replace
00186  *      the code argument as the current completion code for interp.
00187  *
00188  * Side effects:
00189  *      Depends on the handlers that are active.
00190  *
00191  *----------------------------------------------------------------------
00192  */
00193 
00194 int
00195 Tcl_AsyncInvoke(
00196     Tcl_Interp *interp,         /* If invoked from Tcl_Eval just after
00197                                  * completing a command, points to
00198                                  * interpreter. Otherwise it is NULL. */
00199     int code)                   /* If interp is non-NULL, this gives
00200                                  * completion code from command that just
00201                                  * completed. */
00202 {
00203     AsyncHandler *asyncPtr;
00204     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00205 
00206     Tcl_MutexLock(&tsdPtr->asyncMutex);
00207 
00208     if (tsdPtr->asyncReady == 0) {
00209         Tcl_MutexUnlock(&tsdPtr->asyncMutex);
00210         return code;
00211     }
00212     tsdPtr->asyncReady = 0;
00213     tsdPtr->asyncActive = 1;
00214     if (interp == NULL) {
00215         code = 0;
00216     }
00217 
00218     /*
00219      * Make one or more passes over the list of handlers, invoking at most one
00220      * handler in each pass. After invoking a handler, go back to the start of
00221      * the list again so that (a) if a new higher-priority handler gets marked
00222      * while executing a lower priority handler, we execute the higher-
00223      * priority handler next, and (b) if a handler gets deleted during the
00224      * execution of a handler, then the list structure may change so it isn't
00225      * safe to continue down the list anyway.
00226      */
00227 
00228     while (1) {
00229         for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL;
00230                 asyncPtr = asyncPtr->nextPtr) {
00231             if (asyncPtr->ready) {
00232                 break;
00233             }
00234         }
00235         if (asyncPtr == NULL) {
00236             break;
00237         }
00238         asyncPtr->ready = 0;
00239         Tcl_MutexUnlock(&tsdPtr->asyncMutex);
00240         code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
00241         Tcl_MutexLock(&tsdPtr->asyncMutex);
00242     }
00243     tsdPtr->asyncActive = 0;
00244     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
00245     return code;
00246 }
00247 
00248 /*
00249  *----------------------------------------------------------------------
00250  *
00251  * Tcl_AsyncDelete --
00252  *
00253  *      Frees up all the state for an asynchronous handler. The handler should
00254  *      never be used again.
00255  *
00256  * Results:
00257  *      None.
00258  *
00259  * Side effects:
00260  *      The state associated with the handler is deleted.
00261  *
00262  *----------------------------------------------------------------------
00263  */
00264 
00265 void
00266 Tcl_AsyncDelete(
00267     Tcl_AsyncHandler async)             /* Token for handler to delete. */
00268 {
00269     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00270     AsyncHandler *asyncPtr = (AsyncHandler *) async;
00271     AsyncHandler *prevPtr;
00272 
00273     /*
00274      * Conservatively check the existence of the linked list of
00275      * registered handlers, as we may come at this point even
00276      * when the TSD's for the current thread have been already
00277      * garbage-collected.
00278      */
00279 
00280     Tcl_MutexLock(&tsdPtr->asyncMutex);
00281     if (tsdPtr->firstHandler != NULL ) {
00282         if (tsdPtr->firstHandler == asyncPtr) {
00283             tsdPtr->firstHandler = asyncPtr->nextPtr;
00284             if (tsdPtr->firstHandler == NULL) {
00285                 tsdPtr->lastHandler = NULL;
00286             }
00287         } else {
00288             prevPtr = tsdPtr->firstHandler;
00289             while (prevPtr->nextPtr != asyncPtr) {
00290                 prevPtr = prevPtr->nextPtr;
00291             }
00292             prevPtr->nextPtr = asyncPtr->nextPtr;
00293             if (tsdPtr->lastHandler == asyncPtr) {
00294                 tsdPtr->lastHandler = prevPtr;
00295             }
00296         }
00297     }
00298     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
00299     ckfree((char *) asyncPtr);
00300 }
00301 
00302 /*
00303  *----------------------------------------------------------------------
00304  *
00305  * Tcl_AsyncReady --
00306  *
00307  *      This procedure can be used to tell whether Tcl_AsyncInvoke needs to be
00308  *      called. This procedure is the external interface for checking the
00309  *      thread-specific asyncReady variable.
00310  *
00311  * Results:
00312  *      The return value is 1 whenever a handler is ready and is 0 when no
00313  *      handlers are ready.
00314  *
00315  * Side effects:
00316  *      None.
00317  *
00318  *----------------------------------------------------------------------
00319  */
00320 
00321 int
00322 Tcl_AsyncReady(void)
00323 {
00324     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00325     return tsdPtr->asyncReady;
00326 }
00327 
00328 int *
00329 TclGetAsyncReadyPtr(void)
00330 {
00331     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00332     return &(tsdPtr->asyncReady);
00333 }
00334 
00335 /*
00336  * Local Variables:
00337  * mode: c
00338  * c-basic-offset: 4
00339  * fill-column: 78
00340  * End:
00341  */



Generated on Wed Mar 12 12:18:11 2008 by  doxygen 1.5.1