4 * This file provides low-level support needed to invoke signal
5 * handlers in a safe way. The code here doesn't actually handle
6 * signals, though. This code is based on proposals made by
7 * Mark Diekhans and Don Libes.
9 * Copyright (c) 1993 The Regents of the University of California.
10 * Copyright (c) 1994 Sun Microsystems, Inc.
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 /* Forward declaration */
22 struct ThreadSpecificData;
25 * One of the following structures exists for each asynchronous
29 typedef struct AsyncHandler {
30 int ready; /* Non-zero means this handler should
31 * be invoked in the next call to
33 struct AsyncHandler *nextPtr; /* Next in list of all handlers for
35 Tcl_AsyncProc *proc; /* Procedure to call when handler
37 ClientData clientData; /* Value to pass to handler when it
39 struct ThreadSpecificData *originTsd;
40 /* Used in Tcl_AsyncMark to modify thread-
41 * specific data from outside the thread
42 * it is associated to. */
43 Tcl_ThreadId originThrdId; /* Origin thread where this token was
44 * created and where it will be
49 typedef struct ThreadSpecificData {
51 * The variables below maintain a list of all existing handlers
52 * specific to the calling thread.
54 AsyncHandler *firstHandler; /* First handler defined for process,
56 AsyncHandler *lastHandler; /* Last handler or NULL. */
59 * The variable below is set to 1 whenever a handler becomes ready and
60 * it is cleared to zero whenever Tcl_AsyncInvoke is called. It can be
61 * checked elsewhere in the application by calling Tcl_AsyncReady to see
62 * if Tcl_AsyncInvoke should be invoked.
68 * The variable below indicates whether Tcl_AsyncInvoke is currently
69 * working. If so then we won't set asyncReady again until
70 * Tcl_AsyncInvoke returns.
75 Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list lock */
78 static Tcl_ThreadDataKey dataKey;
82 *----------------------------------------------------------------------
86 * Finalizes the mutex in the thread local data structure for the
93 * Forgets knowledge of the mutex should it have been created.
95 *----------------------------------------------------------------------
101 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
103 if (tsdPtr->asyncMutex != NULL) {
104 Tcl_MutexFinalize(&tsdPtr->asyncMutex);
109 *----------------------------------------------------------------------
113 * This procedure creates the data structures for an asynchronous
114 * handler, so that no memory has to be allocated when the handler
118 * The return value is a token for the handler, which can be used
119 * to activate it later on.
122 * Information about the handler is recorded.
124 *----------------------------------------------------------------------
128 Tcl_AsyncCreate(proc, clientData)
129 Tcl_AsyncProc *proc; /* Procedure to call when handler
131 ClientData clientData; /* Argument to pass to handler. */
133 AsyncHandler *asyncPtr;
134 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
136 asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
138 asyncPtr->nextPtr = NULL;
139 asyncPtr->proc = proc;
140 asyncPtr->clientData = clientData;
141 asyncPtr->originTsd = tsdPtr;
142 asyncPtr->originThrdId = Tcl_GetCurrentThread();
144 Tcl_MutexLock(&tsdPtr->asyncMutex);
145 if (tsdPtr->firstHandler == NULL) {
146 tsdPtr->firstHandler = asyncPtr;
148 tsdPtr->lastHandler->nextPtr = asyncPtr;
150 tsdPtr->lastHandler = asyncPtr;
151 Tcl_MutexUnlock(&tsdPtr->asyncMutex);
152 return (Tcl_AsyncHandler) asyncPtr;
156 *----------------------------------------------------------------------
160 * This procedure is called to request that an asynchronous handler
161 * be invoked as soon as possible. It's typically called from
162 * an interrupt handler, where it isn't safe to do anything that
163 * depends on or modifies application state.
169 * The handler gets marked for invocation later.
171 *----------------------------------------------------------------------
176 Tcl_AsyncHandler async; /* Token for handler. */
178 AsyncHandler *token = (AsyncHandler *) async;
180 Tcl_MutexLock(&token->originTsd->asyncMutex);
182 if (!token->originTsd->asyncActive) {
183 token->originTsd->asyncReady = 1;
184 Tcl_ThreadAlert(token->originThrdId);
186 Tcl_MutexUnlock(&token->originTsd->asyncMutex);
190 *----------------------------------------------------------------------
194 * This procedure is called at a "safe" time at background level
195 * to invoke any active asynchronous handlers.
198 * The return value is a normal Tcl result, which is intended to
199 * replace the code argument as the current completion code for
203 * Depends on the handlers that are active.
205 *----------------------------------------------------------------------
209 Tcl_AsyncInvoke(interp, code)
210 Tcl_Interp *interp; /* If invoked from Tcl_Eval just after
211 * completing a command, points to
212 * interpreter. Otherwise it is
214 int code; /* If interp is non-NULL, this gives
215 * completion code from command that
218 AsyncHandler *asyncPtr;
219 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
221 Tcl_MutexLock(&tsdPtr->asyncMutex);
223 if (tsdPtr->asyncReady == 0) {
224 Tcl_MutexUnlock(&tsdPtr->asyncMutex);
227 tsdPtr->asyncReady = 0;
228 tsdPtr->asyncActive = 1;
229 if (interp == NULL) {
234 * Make one or more passes over the list of handlers, invoking
235 * at most one handler in each pass. After invoking a handler,
236 * go back to the start of the list again so that (a) if a new
237 * higher-priority handler gets marked while executing a lower
238 * priority handler, we execute the higher-priority handler
239 * next, and (b) if a handler gets deleted during the execution
240 * of a handler, then the list structure may change so it isn't
241 * safe to continue down the list anyway.
245 for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL;
246 asyncPtr = asyncPtr->nextPtr) {
247 if (asyncPtr->ready) {
251 if (asyncPtr == NULL) {
255 Tcl_MutexUnlock(&tsdPtr->asyncMutex);
256 code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
257 Tcl_MutexLock(&tsdPtr->asyncMutex);
259 tsdPtr->asyncActive = 0;
260 Tcl_MutexUnlock(&tsdPtr->asyncMutex);
265 *----------------------------------------------------------------------
269 * Frees up all the state for an asynchronous handler. The handler
270 * should never be used again.
276 * The state associated with the handler is deleted.
278 *----------------------------------------------------------------------
282 Tcl_AsyncDelete(async)
283 Tcl_AsyncHandler async; /* Token for handler to delete. */
285 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
286 AsyncHandler *asyncPtr = (AsyncHandler *) async;
287 AsyncHandler *prevPtr;
289 Tcl_MutexLock(&tsdPtr->asyncMutex);
290 if (tsdPtr->firstHandler == asyncPtr) {
291 tsdPtr->firstHandler = asyncPtr->nextPtr;
292 if (tsdPtr->firstHandler == NULL) {
293 tsdPtr->lastHandler = NULL;
296 prevPtr = tsdPtr->firstHandler;
297 while (prevPtr->nextPtr != asyncPtr) {
298 prevPtr = prevPtr->nextPtr;
300 prevPtr->nextPtr = asyncPtr->nextPtr;
301 if (tsdPtr->lastHandler == asyncPtr) {
302 tsdPtr->lastHandler = prevPtr;
305 Tcl_MutexUnlock(&tsdPtr->asyncMutex);
306 ckfree((char *) asyncPtr);
310 *----------------------------------------------------------------------
314 * This procedure can be used to tell whether Tcl_AsyncInvoke
315 * needs to be called. This procedure is the external interface
316 * for checking the thread-specific asyncReady variable.
319 * The return value is 1 whenever a handler is ready and is 0
320 * when no handlers are ready.
325 *----------------------------------------------------------------------
331 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
332 return tsdPtr->asyncReady;