Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclAsync.c @ 63

Last change on this file since 63 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 9.3 KB
Line 
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 */
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 *----------------------------------------------------------------------
263 */
264
265void
266Tcl_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
321int
322Tcl_AsyncReady(void)
323{
324    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
325    return tsdPtr->asyncReady;
326}
327
328int *
329TclGetAsyncReadyPtr(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 */
Note: See TracBrowser for help on using the repository browser.