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