OSDN Git Service

Updated to tcl 8.4.1
[pf3gnuchains/sourceware.git] / tcl / generic / tclAsync.c
1 /* 
2  * tclAsync.c --
3  *
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.
8  *
9  * Copyright (c) 1993 The Regents of the University of California.
10  * Copyright (c) 1994 Sun Microsystems, Inc.
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  *
15  * RCS: @(#) $Id$
16  */
17
18 #include "tclInt.h"
19 #include "tclPort.h"
20
21 /* Forward declaration */
22 struct ThreadSpecificData;
23
24 /*
25  * One of the following structures exists for each asynchronous
26  * handler:
27  */
28
29 typedef struct AsyncHandler {
30     int ready;                          /* Non-zero means this handler should
31                                          * be invoked in the next call to
32                                          * Tcl_AsyncInvoke. */
33     struct AsyncHandler *nextPtr;       /* Next in list of all handlers for
34                                          * the process. */
35     Tcl_AsyncProc *proc;                /* Procedure to call when handler
36                                          * is invoked. */
37     ClientData clientData;              /* Value to pass to handler when it
38                                          * is invoked. */
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
45                                          * yielded. */
46 } AsyncHandler;
47
48
49 typedef struct ThreadSpecificData {
50     /*
51      * The variables below maintain a list of all existing handlers
52      * specific to the calling thread.
53      */
54     AsyncHandler *firstHandler;     /* First handler defined for process,
55                                      * or NULL if none. */
56     AsyncHandler *lastHandler;      /* Last handler or NULL. */
57
58     /*
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.
63      */
64
65     int asyncReady;
66
67     /*
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.
71      */
72
73     int asyncActive;
74
75     Tcl_Mutex asyncMutex;   /* Thread-specific AsyncHandler linked-list lock */
76
77 } ThreadSpecificData;
78 static Tcl_ThreadDataKey dataKey;
79
80 \f
81 /*
82  *----------------------------------------------------------------------
83  *
84  * TclFinalizeAsync --
85  *
86  *      Finalizes the mutex in the thread local data structure for the
87  *      async subsystem.
88  *
89  * Results:
90  *      None.   
91  *
92  * Side effects:
93  *      Forgets knowledge of the mutex should it have been created.
94  *
95  *----------------------------------------------------------------------
96  */
97
98 void
99 TclFinalizeAsync()
100 {
101     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
102
103     if (tsdPtr->asyncMutex != NULL) {
104         Tcl_MutexFinalize(&tsdPtr->asyncMutex);
105     }
106 }
107 \f
108 /*
109  *----------------------------------------------------------------------
110  *
111  * Tcl_AsyncCreate --
112  *
113  *      This procedure creates the data structures for an asynchronous
114  *      handler, so that no memory has to be allocated when the handler
115  *      is activated.
116  *
117  * Results:
118  *      The return value is a token for the handler, which can be used
119  *      to activate it later on.
120  *
121  * Side effects:
122  *      Information about the handler is recorded.
123  *
124  *----------------------------------------------------------------------
125  */
126
127 Tcl_AsyncHandler
128 Tcl_AsyncCreate(proc, clientData)
129     Tcl_AsyncProc *proc;                /* Procedure to call when handler
130                                          * is invoked. */
131     ClientData clientData;              /* Argument to pass to handler. */
132 {
133     AsyncHandler *asyncPtr;
134     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
135
136     asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
137     asyncPtr->ready = 0;
138     asyncPtr->nextPtr = NULL;
139     asyncPtr->proc = proc;
140     asyncPtr->clientData = clientData;
141     asyncPtr->originTsd = tsdPtr;
142     asyncPtr->originThrdId = Tcl_GetCurrentThread();
143
144     Tcl_MutexLock(&tsdPtr->asyncMutex);
145     if (tsdPtr->firstHandler == NULL) {
146         tsdPtr->firstHandler = asyncPtr;
147     } else {
148         tsdPtr->lastHandler->nextPtr = asyncPtr;
149     }
150     tsdPtr->lastHandler = asyncPtr;
151     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
152     return (Tcl_AsyncHandler) asyncPtr;
153 }
154 \f
155 /*
156  *----------------------------------------------------------------------
157  *
158  * Tcl_AsyncMark --
159  *
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.
164  *
165  * Results:
166  *      None.
167  *
168  * Side effects:
169  *      The handler gets marked for invocation later.
170  *
171  *----------------------------------------------------------------------
172  */
173
174 void
175 Tcl_AsyncMark(async)
176     Tcl_AsyncHandler async;             /* Token for handler. */
177 {
178     AsyncHandler *token = (AsyncHandler *) async;
179
180     Tcl_MutexLock(&token->originTsd->asyncMutex);
181     token->ready = 1;
182     if (!token->originTsd->asyncActive) {
183         token->originTsd->asyncReady = 1;
184         Tcl_ThreadAlert(token->originThrdId);
185     }
186     Tcl_MutexUnlock(&token->originTsd->asyncMutex);
187 }
188 \f
189 /*
190  *----------------------------------------------------------------------
191  *
192  * Tcl_AsyncInvoke --
193  *
194  *      This procedure is called at a "safe" time at background level
195  *      to invoke any active asynchronous handlers.
196  *
197  * Results:
198  *      The return value is a normal Tcl result, which is intended to
199  *      replace the code argument as the current completion code for
200  *      interp.
201  *
202  * Side effects:
203  *      Depends on the handlers that are active.
204  *
205  *----------------------------------------------------------------------
206  */
207
208 int
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
213                                          * NULL. */
214     int code;                           /* If interp is non-NULL, this gives
215                                          * completion code from command that
216                                          * just completed. */
217 {
218     AsyncHandler *asyncPtr;
219     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
220
221     Tcl_MutexLock(&tsdPtr->asyncMutex);
222
223     if (tsdPtr->asyncReady == 0) {
224         Tcl_MutexUnlock(&tsdPtr->asyncMutex);
225         return code;
226     }
227     tsdPtr->asyncReady = 0;
228     tsdPtr->asyncActive = 1;
229     if (interp == NULL) {
230         code = 0;
231     }
232
233     /*
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.
242      */
243
244     while (1) {
245         for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL;
246                 asyncPtr = asyncPtr->nextPtr) {
247             if (asyncPtr->ready) {
248                 break;
249             }
250         }
251         if (asyncPtr == NULL) {
252             break;
253         }
254         asyncPtr->ready = 0;
255         Tcl_MutexUnlock(&tsdPtr->asyncMutex);
256         code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
257         Tcl_MutexLock(&tsdPtr->asyncMutex);
258     }
259     tsdPtr->asyncActive = 0;
260     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
261     return code;
262 }
263 \f
264 /*
265  *----------------------------------------------------------------------
266  *
267  * Tcl_AsyncDelete --
268  *
269  *      Frees up all the state for an asynchronous handler.  The handler
270  *      should never be used again.
271  *
272  * Results:
273  *      None.
274  *
275  * Side effects:
276  *      The state associated with the handler is deleted.
277  *
278  *----------------------------------------------------------------------
279  */
280
281 void
282 Tcl_AsyncDelete(async)
283     Tcl_AsyncHandler async;             /* Token for handler to delete. */
284 {
285     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
286     AsyncHandler *asyncPtr = (AsyncHandler *) async;
287     AsyncHandler *prevPtr;
288
289     Tcl_MutexLock(&tsdPtr->asyncMutex);
290     if (tsdPtr->firstHandler == asyncPtr) {
291         tsdPtr->firstHandler = asyncPtr->nextPtr;
292         if (tsdPtr->firstHandler == NULL) {
293             tsdPtr->lastHandler = NULL;
294         }
295     } else {
296         prevPtr = tsdPtr->firstHandler;
297         while (prevPtr->nextPtr != asyncPtr) {
298             prevPtr = prevPtr->nextPtr;
299         }
300         prevPtr->nextPtr = asyncPtr->nextPtr;
301         if (tsdPtr->lastHandler == asyncPtr) {
302             tsdPtr->lastHandler = prevPtr;
303         }
304     }
305     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
306     ckfree((char *) asyncPtr);
307 }
308 \f
309 /*
310  *----------------------------------------------------------------------
311  *
312  * Tcl_AsyncReady --
313  *
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.
317  *
318  * Results:
319  *      The return value is 1 whenever a handler is ready and is 0
320  *      when no handlers are ready.
321  *
322  * Side effects:
323  *      None.
324  *
325  *----------------------------------------------------------------------
326  */
327
328 int
329 Tcl_AsyncReady()
330 {
331     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
332     return tsdPtr->asyncReady;
333 }