| 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 2007/12/13 15:23:14 dgp Exp $ | 
|---|
| 15 |  */ | 
|---|
| 16 |  | 
|---|
| 17 | #include "tclInt.h" | 
|---|
| 18 |  | 
|---|
| 19 | /* Forward declaration */ | 
|---|
| 20 | struct ThreadSpecificData; | 
|---|
| 21 |  | 
|---|
| 22 | /* | 
|---|
| 23 |  * One of the following structures exists for each asynchronous handler: | 
|---|
| 24 |  */ | 
|---|
| 25 |  | 
|---|
| 26 | typedef 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 |  | 
|---|
| 45 | typedef 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; | 
|---|
| 66 | static 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 |  | 
|---|
| 85 | void | 
|---|
| 86 | TclFinalizeAsync(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 |  | 
|---|
| 114 | Tcl_AsyncHandler | 
|---|
| 115 | Tcl_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 |  | 
|---|
| 161 | void | 
|---|
| 162 | Tcl_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 |  | 
|---|
| 194 | int | 
|---|
| 195 | Tcl_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 |  *---------------------------------------------------------------------- | 
|---|
| 263 |  */ | 
|---|
| 264 |  | 
|---|
| 265 | void | 
|---|
| 266 | Tcl_AsyncDelete( | 
|---|
| 267 |     Tcl_AsyncHandler async)             /* Token for handler to delete. */ | 
|---|
| 268 | { | 
|---|
| 269 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 270 |     AsyncHandler *asyncPtr = (AsyncHandler *) async; | 
|---|
| 271 |     AsyncHandler *prevPtr; | 
|---|
| 272 |  | 
|---|
| 273 |     /* | 
|---|
| 274 |      * Conservatively check the existence of the linked list of | 
|---|
| 275 |      * registered handlers, as we may come at this point even | 
|---|
| 276 |      * when the TSD's for the current thread have been already | 
|---|
| 277 |      * garbage-collected. | 
|---|
| 278 |      */ | 
|---|
| 279 |  | 
|---|
| 280 |     Tcl_MutexLock(&tsdPtr->asyncMutex); | 
|---|
| 281 |     if (tsdPtr->firstHandler != NULL ) { | 
|---|
| 282 |         if (tsdPtr->firstHandler == asyncPtr) { | 
|---|
| 283 |             tsdPtr->firstHandler = asyncPtr->nextPtr; | 
|---|
| 284 |             if (tsdPtr->firstHandler == NULL) { | 
|---|
| 285 |                 tsdPtr->lastHandler = NULL; | 
|---|
| 286 |             } | 
|---|
| 287 |         } else { | 
|---|
| 288 |             prevPtr = tsdPtr->firstHandler; | 
|---|
| 289 |             while (prevPtr->nextPtr != asyncPtr) { | 
|---|
| 290 |                 prevPtr = prevPtr->nextPtr; | 
|---|
| 291 |             } | 
|---|
| 292 |             prevPtr->nextPtr = asyncPtr->nextPtr; | 
|---|
| 293 |             if (tsdPtr->lastHandler == asyncPtr) { | 
|---|
| 294 |                 tsdPtr->lastHandler = prevPtr; | 
|---|
| 295 |             } | 
|---|
| 296 |         } | 
|---|
| 297 |     } | 
|---|
| 298 |     Tcl_MutexUnlock(&tsdPtr->asyncMutex); | 
|---|
| 299 |     ckfree((char *) asyncPtr); | 
|---|
| 300 | } | 
|---|
| 301 |  | 
|---|
| 302 | /* | 
|---|
| 303 |  *---------------------------------------------------------------------- | 
|---|
| 304 |  * | 
|---|
| 305 |  * Tcl_AsyncReady -- | 
|---|
| 306 |  * | 
|---|
| 307 |  *      This procedure can be used to tell whether Tcl_AsyncInvoke needs to be | 
|---|
| 308 |  *      called. This procedure is the external interface for checking the | 
|---|
| 309 |  *      thread-specific asyncReady variable. | 
|---|
| 310 |  * | 
|---|
| 311 |  * Results: | 
|---|
| 312 |  *      The return value is 1 whenever a handler is ready and is 0 when no | 
|---|
| 313 |  *      handlers are ready. | 
|---|
| 314 |  * | 
|---|
| 315 |  * Side effects: | 
|---|
| 316 |  *      None. | 
|---|
| 317 |  * | 
|---|
| 318 |  *---------------------------------------------------------------------- | 
|---|
| 319 |  */ | 
|---|
| 320 |  | 
|---|
| 321 | int | 
|---|
| 322 | Tcl_AsyncReady(void) | 
|---|
| 323 | { | 
|---|
| 324 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 325 |     return tsdPtr->asyncReady; | 
|---|
| 326 | } | 
|---|
| 327 |  | 
|---|
| 328 | int * | 
|---|
| 329 | TclGetAsyncReadyPtr(void) | 
|---|
| 330 | { | 
|---|
| 331 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 332 |     return &(tsdPtr->asyncReady); | 
|---|
| 333 | } | 
|---|
| 334 |  | 
|---|
| 335 | /* | 
|---|
| 336 |  * Local Variables: | 
|---|
| 337 |  * mode: c | 
|---|
| 338 |  * c-basic-offset: 4 | 
|---|
| 339 |  * fill-column: 78 | 
|---|
| 340 |  * End: | 
|---|
| 341 |  */ | 
|---|