1/* 2 * tclAsync.c -- 3 * 4 * This file provides low-level support needed to invoke signal handlers 5 * in a safe way. The code here doesn't actually handle signals, though. 6 * This code is based on proposals made by Mark Diekhans and Don Libes. 7 * 8 * Copyright (c) 1993 The Regents of the University of California. 9 * Copyright (c) 1994 Sun Microsystems, Inc. 10 * 11 * See the file "license.terms" for information on usage and redistribution of 12 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id: tclAsync.c,v 1.13.2.3 2008/05/03 19:31:28 das Exp $ 15 */ 16 17#include "tclInt.h" 18 19/* Forward declaration */ 20struct ThreadSpecificData; 21 22/* 23 * One of the following structures exists for each asynchronous handler: 24 */ 25 26typedef struct AsyncHandler { 27 int ready; /* Non-zero means this handler should be 28 * invoked in the next call to 29 * Tcl_AsyncInvoke. */ 30 struct AsyncHandler *nextPtr; 31 /* Next in list of all handlers for the 32 * process. */ 33 Tcl_AsyncProc *proc; /* Procedure to call when handler is 34 * invoked. */ 35 ClientData clientData; /* Value to pass to handler when it is 36 * invoked. */ 37 struct ThreadSpecificData *originTsd; 38 /* Used in Tcl_AsyncMark to modify thread- 39 * specific data from outside the thread it is 40 * associated to. */ 41 Tcl_ThreadId originThrdId; /* Origin thread where this token was created 42 * and where it will be yielded. */ 43} AsyncHandler; 44 45typedef struct ThreadSpecificData { 46 /* 47 * The variables below maintain a list of all existing handlers specific 48 * to the calling thread. 49 */ 50 AsyncHandler *firstHandler; /* First handler defined for process, or NULL 51 * if none. */ 52 AsyncHandler *lastHandler; /* Last handler or NULL. */ 53 int asyncReady; /* This is set to 1 whenever a handler becomes 54 * ready and it is cleared to zero whenever 55 * Tcl_AsyncInvoke is called. It can be 56 * checked elsewhere in the application by 57 * calling Tcl_AsyncReady to see if 58 * Tcl_AsyncInvoke should be invoked. */ 59 int asyncActive; /* Indicates whether Tcl_AsyncInvoke is 60 * currently working. If so then we won't set 61 * asyncReady again until Tcl_AsyncInvoke 62 * returns. */ 63 Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list 64 * lock */ 65} ThreadSpecificData; 66static Tcl_ThreadDataKey dataKey; 67 68/* 69 *---------------------------------------------------------------------- 70 * 71 * TclFinalizeAsync -- 72 * 73 * Finalizes the mutex in the thread local data structure for the async 74 * subsystem. 75 * 76 * Results: 77 * None. 78 * 79 * Side effects: 80 * Forgets knowledge of the mutex should it have been created. 81 * 82 *---------------------------------------------------------------------- 83 */ 84 85void 86TclFinalizeAsync(void) 87{ 88 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 89 90 if (tsdPtr->asyncMutex != NULL) { 91 Tcl_MutexFinalize(&tsdPtr->asyncMutex); 92 } 93} 94 95/* 96 *---------------------------------------------------------------------- 97 * 98 * Tcl_AsyncCreate -- 99 * 100 * This procedure creates the data structures for an asynchronous 101 * handler, so that no memory has to be allocated when the handler is 102 * activated. 103 * 104 * Results: 105 * The return value is a token for the handler, which can be used to 106 * activate it later on. 107 * 108 * Side effects: 109 * Information about the handler is recorded. 110 * 111 *---------------------------------------------------------------------- 112 */ 113 114Tcl_AsyncHandler 115Tcl_AsyncCreate( 116 Tcl_AsyncProc *proc, /* Procedure to call when handler is 117 * invoked. */ 118 ClientData clientData) /* Argument to pass to handler. */ 119{ 120 AsyncHandler *asyncPtr; 121 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 122 123 asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler)); 124 asyncPtr->ready = 0; 125 asyncPtr->nextPtr = NULL; 126 asyncPtr->proc = proc; 127 asyncPtr->clientData = clientData; 128 asyncPtr->originTsd = tsdPtr; 129 asyncPtr->originThrdId = Tcl_GetCurrentThread(); 130 131 Tcl_MutexLock(&tsdPtr->asyncMutex); 132 if (tsdPtr->firstHandler == NULL) { 133 tsdPtr->firstHandler = asyncPtr; 134 } else { 135 tsdPtr->lastHandler->nextPtr = asyncPtr; 136 } 137 tsdPtr->lastHandler = asyncPtr; 138 Tcl_MutexUnlock(&tsdPtr->asyncMutex); 139 return (Tcl_AsyncHandler) asyncPtr; 140} 141 142/* 143 *---------------------------------------------------------------------- 144 * 145 * Tcl_AsyncMark -- 146 * 147 * This procedure is called to request that an asynchronous handler be 148 * invoked as soon as possible. It's typically called from an interrupt 149 * handler, where it isn't safe to do anything that depends on or 150 * modifies application state. 151 * 152 * Results: 153 * None. 154 * 155 * Side effects: 156 * The handler gets marked for invocation later. 157 * 158 *---------------------------------------------------------------------- 159 */ 160 161void 162Tcl_AsyncMark( 163 Tcl_AsyncHandler async) /* Token for handler. */ 164{ 165 AsyncHandler *token = (AsyncHandler *) async; 166 167 Tcl_MutexLock(&token->originTsd->asyncMutex); 168 token->ready = 1; 169 if (!token->originTsd->asyncActive) { 170 token->originTsd->asyncReady = 1; 171 Tcl_ThreadAlert(token->originThrdId); 172 } 173 Tcl_MutexUnlock(&token->originTsd->asyncMutex); 174} 175 176/* 177 *---------------------------------------------------------------------- 178 * 179 * Tcl_AsyncInvoke -- 180 * 181 * This procedure is called at a "safe" time at background level to 182 * invoke any active asynchronous handlers. 183 * 184 * Results: 185 * The return value is a normal Tcl result, which is intended to replace 186 * the code argument as the current completion code for interp. 187 * 188 * Side effects: 189 * Depends on the handlers that are active. 190 * 191 *---------------------------------------------------------------------- 192 */ 193 194int 195Tcl_AsyncInvoke( 196 Tcl_Interp *interp, /* If invoked from Tcl_Eval just after 197 * completing a command, points to 198 * interpreter. Otherwise it is NULL. */ 199 int code) /* If interp is non-NULL, this gives 200 * completion code from command that just 201 * completed. */ 202{ 203 AsyncHandler *asyncPtr; 204 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 205 206 Tcl_MutexLock(&tsdPtr->asyncMutex); 207 208 if (tsdPtr->asyncReady == 0) { 209 Tcl_MutexUnlock(&tsdPtr->asyncMutex); 210 return code; 211 } 212 tsdPtr->asyncReady = 0; 213 tsdPtr->asyncActive = 1; 214 if (interp == NULL) { 215 code = 0; 216 } 217 218 /* 219 * Make one or more passes over the list of handlers, invoking at most one 220 * handler in each pass. After invoking a handler, go back to the start of 221 * the list again so that (a) if a new higher-priority handler gets marked 222 * while executing a lower priority handler, we execute the higher- 223 * priority handler next, and (b) if a handler gets deleted during the 224 * execution of a handler, then the list structure may change so it isn't 225 * safe to continue down the list anyway. 226 */ 227 228 while (1) { 229 for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL; 230 asyncPtr = asyncPtr->nextPtr) { 231 if (asyncPtr->ready) { 232 break; 233 } 234 } 235 if (asyncPtr == NULL) { 236 break; 237 } 238 asyncPtr->ready = 0; 239 Tcl_MutexUnlock(&tsdPtr->asyncMutex); 240 code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code); 241 Tcl_MutexLock(&tsdPtr->asyncMutex); 242 } 243 tsdPtr->asyncActive = 0; 244 Tcl_MutexUnlock(&tsdPtr->asyncMutex); 245 return code; 246} 247 248/* 249 *---------------------------------------------------------------------- 250 * 251 * Tcl_AsyncDelete -- 252 * 253 * Frees up all the state for an asynchronous handler. The handler should 254 * never be used again. 255 * 256 * Results: 257 * None. 258 * 259 * Side effects: 260 * The state associated with the handler is deleted. 261 * 262 * Failure to locate the handler in current thread private list 263 * of async handlers will result in panic; exception: the list 264 * is already empty (potential trouble?). 265 * Consequently, threads should create and delete handlers 266 * themselves. I.e. a handler created by one should not be 267 * deleted by some other thread. 268 * 269 *---------------------------------------------------------------------- 270 */ 271 272void 273Tcl_AsyncDelete( 274 Tcl_AsyncHandler async) /* Token for handler to delete. */ 275{ 276 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 277 AsyncHandler *asyncPtr = (AsyncHandler *) async; 278 AsyncHandler *prevPtr, *thisPtr; 279 280 /* 281 * Assure early handling of the constraint 282 */ 283 284 if (asyncPtr->originThrdId != Tcl_GetCurrentThread()) { 285 Tcl_Panic("Tcl_AsyncDelete: async handler deleted by the wrong thread"); 286 } 287 288 /* 289 * If we come to this point when TSD's for the current 290 * thread have already been garbage-collected, we are 291 * in the _serious_ trouble. OTOH, we tolerate calling 292 * with already cleaned-up handler list (should we?). 293 */ 294 295 Tcl_MutexLock(&tsdPtr->asyncMutex); 296 if (tsdPtr->firstHandler != NULL) { 297 prevPtr = thisPtr = tsdPtr->firstHandler; 298 while (thisPtr != NULL && thisPtr != asyncPtr) { 299 prevPtr = thisPtr; 300 thisPtr = thisPtr->nextPtr; 301 } 302 if (thisPtr == NULL) { 303 Tcl_Panic("Tcl_AsyncDelete: cannot find async handler"); 304 } 305 if (asyncPtr == tsdPtr->firstHandler) { 306 tsdPtr->firstHandler = asyncPtr->nextPtr; 307 } else { 308 prevPtr->nextPtr = asyncPtr->nextPtr; 309 } 310 if (asyncPtr == tsdPtr->lastHandler) { 311 tsdPtr->lastHandler = prevPtr; 312 } 313 } 314 Tcl_MutexUnlock(&tsdPtr->asyncMutex); 315 ckfree((char *) asyncPtr); 316} 317 318/* 319 *---------------------------------------------------------------------- 320 * 321 * Tcl_AsyncReady -- 322 * 323 * This procedure can be used to tell whether Tcl_AsyncInvoke needs to be 324 * called. This procedure is the external interface for checking the 325 * thread-specific asyncReady variable. 326 * 327 * Results: 328 * The return value is 1 whenever a handler is ready and is 0 when no 329 * handlers are ready. 330 * 331 * Side effects: 332 * None. 333 * 334 *---------------------------------------------------------------------- 335 */ 336 337int 338Tcl_AsyncReady(void) 339{ 340 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 341 return tsdPtr->asyncReady; 342} 343 344int * 345TclGetAsyncReadyPtr(void) 346{ 347 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 348 return &(tsdPtr->asyncReady); 349} 350 351/* 352 * Local Variables: 353 * mode: c 354 * c-basic-offset: 4 355 * fill-column: 78 356 * End: 357 */ 358