tclAsync.cGo 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 1.5.1 |