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: tclAsync.c,v 1.1 2008-06-04 13:58:03 demin Exp $
|
---|
16 | */
|
---|
17 |
|
---|
18 | #include "tclInt.h"
|
---|
19 |
|
---|
20 | /*
|
---|
21 | * One of the following structures exists for each asynchronous
|
---|
22 | * handler:
|
---|
23 | */
|
---|
24 |
|
---|
25 | typedef struct AsyncHandler {
|
---|
26 | int ready; /* Non-zero means this handler should
|
---|
27 | * be invoked in the next call to
|
---|
28 | * Tcl_AsyncInvoke. */
|
---|
29 | struct AsyncHandler *nextPtr; /* Next in list of all handlers for
|
---|
30 | * the process. */
|
---|
31 | Tcl_AsyncProc *proc; /* Procedure to call when handler
|
---|
32 | * is invoked. */
|
---|
33 | ClientData clientData; /* Value to pass to handler when it
|
---|
34 | * is invoked. */
|
---|
35 | } AsyncHandler;
|
---|
36 |
|
---|
37 | /*
|
---|
38 | * The variables below maintain a list of all existing handlers.
|
---|
39 | */
|
---|
40 |
|
---|
41 | static AsyncHandler *firstHandler; /* First handler defined for process,
|
---|
42 | * or NULL if none. */
|
---|
43 | static AsyncHandler *lastHandler; /* Last handler or NULL. */
|
---|
44 |
|
---|
45 | /*
|
---|
46 | * The variable below is set to 1 whenever a handler becomes ready and
|
---|
47 | * it is cleared to zero whenever Tcl_AsyncInvoke is called. It can be
|
---|
48 | * checked elsewhere in the application by calling Tcl_AsyncReady to see
|
---|
49 | * if Tcl_AsyncInvoke should be invoked.
|
---|
50 | */
|
---|
51 |
|
---|
52 | static int asyncReady = 0;
|
---|
53 |
|
---|
54 | /*
|
---|
55 | * The variable below indicates whether Tcl_AsyncInvoke is currently
|
---|
56 | * working. If so then we won't set asyncReady again until
|
---|
57 | * Tcl_AsyncInvoke returns.
|
---|
58 | */
|
---|
59 |
|
---|
60 | static int asyncActive = 0;
|
---|
61 | |
---|
62 |
|
---|
63 | /*
|
---|
64 | *----------------------------------------------------------------------
|
---|
65 | *
|
---|
66 | * Tcl_AsyncCreate --
|
---|
67 | *
|
---|
68 | * This procedure creates the data structures for an asynchronous
|
---|
69 | * handler, so that no memory has to be allocated when the handler
|
---|
70 | * is activated.
|
---|
71 | *
|
---|
72 | * Results:
|
---|
73 | * The return value is a token for the handler, which can be used
|
---|
74 | * to activate it later on.
|
---|
75 | *
|
---|
76 | * Side effects:
|
---|
77 | * Information about the handler is recorded.
|
---|
78 | *
|
---|
79 | *----------------------------------------------------------------------
|
---|
80 | */
|
---|
81 |
|
---|
82 | Tcl_AsyncHandler
|
---|
83 | Tcl_AsyncCreate(proc, clientData)
|
---|
84 | Tcl_AsyncProc *proc; /* Procedure to call when handler
|
---|
85 | * is invoked. */
|
---|
86 | ClientData clientData; /* Argument to pass to handler. */
|
---|
87 | {
|
---|
88 | AsyncHandler *asyncPtr;
|
---|
89 |
|
---|
90 | asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
|
---|
91 | asyncPtr->ready = 0;
|
---|
92 | asyncPtr->nextPtr = NULL;
|
---|
93 | asyncPtr->proc = proc;
|
---|
94 | asyncPtr->clientData = clientData;
|
---|
95 | if (firstHandler == NULL) {
|
---|
96 | firstHandler = asyncPtr;
|
---|
97 | } else {
|
---|
98 | lastHandler->nextPtr = asyncPtr;
|
---|
99 | }
|
---|
100 | lastHandler = asyncPtr;
|
---|
101 | return (Tcl_AsyncHandler) asyncPtr;
|
---|
102 | }
|
---|
103 | |
---|
104 |
|
---|
105 | /*
|
---|
106 | *----------------------------------------------------------------------
|
---|
107 | *
|
---|
108 | * Tcl_AsyncMark --
|
---|
109 | *
|
---|
110 | * This procedure is called to request that an asynchronous handler
|
---|
111 | * be invoked as soon as possible. It's typically called from
|
---|
112 | * an interrupt handler, where it isn't safe to do anything that
|
---|
113 | * depends on or modifies application state.
|
---|
114 | *
|
---|
115 | * Results:
|
---|
116 | * None.
|
---|
117 | *
|
---|
118 | * Side effects:
|
---|
119 | * The handler gets marked for invocation later.
|
---|
120 | *
|
---|
121 | *----------------------------------------------------------------------
|
---|
122 | */
|
---|
123 |
|
---|
124 | void
|
---|
125 | Tcl_AsyncMark(async)
|
---|
126 | Tcl_AsyncHandler async; /* Token for handler. */
|
---|
127 | {
|
---|
128 | ((AsyncHandler *) async)->ready = 1;
|
---|
129 | if (!asyncActive) {
|
---|
130 | asyncReady = 1;
|
---|
131 | }
|
---|
132 | }
|
---|
133 | |
---|
134 |
|
---|
135 | /*
|
---|
136 | *----------------------------------------------------------------------
|
---|
137 | *
|
---|
138 | * Tcl_AsyncInvoke --
|
---|
139 | *
|
---|
140 | * This procedure is called at a "safe" time at background level
|
---|
141 | * to invoke any active asynchronous handlers.
|
---|
142 | *
|
---|
143 | * Results:
|
---|
144 | * The return value is a normal Tcl result, which is intended to
|
---|
145 | * replace the code argument as the current completion code for
|
---|
146 | * interp.
|
---|
147 | *
|
---|
148 | * Side effects:
|
---|
149 | * Depends on the handlers that are active.
|
---|
150 | *
|
---|
151 | *----------------------------------------------------------------------
|
---|
152 | */
|
---|
153 |
|
---|
154 | int
|
---|
155 | Tcl_AsyncInvoke(interp, code)
|
---|
156 | Tcl_Interp *interp; /* If invoked from Tcl_Eval just after
|
---|
157 | * completing a command, points to
|
---|
158 | * interpreter. Otherwise it is
|
---|
159 | * NULL. */
|
---|
160 | int code; /* If interp is non-NULL, this gives
|
---|
161 | * completion code from command that
|
---|
162 | * just completed. */
|
---|
163 | {
|
---|
164 | AsyncHandler *asyncPtr;
|
---|
165 |
|
---|
166 | if (asyncReady == 0) {
|
---|
167 | return code;
|
---|
168 | }
|
---|
169 | asyncReady = 0;
|
---|
170 | asyncActive = 1;
|
---|
171 | if (interp == NULL) {
|
---|
172 | code = 0;
|
---|
173 | }
|
---|
174 |
|
---|
175 | /*
|
---|
176 | * Make one or more passes over the list of handlers, invoking
|
---|
177 | * at most one handler in each pass. After invoking a handler,
|
---|
178 | * go back to the start of the list again so that (a) if a new
|
---|
179 | * higher-priority handler gets marked while executing a lower
|
---|
180 | * priority handler, we execute the higher-priority handler
|
---|
181 | * next, and (b) if a handler gets deleted during the execution
|
---|
182 | * of a handler, then the list structure may change so it isn't
|
---|
183 | * safe to continue down the list anyway.
|
---|
184 | */
|
---|
185 |
|
---|
186 | while (1) {
|
---|
187 | for (asyncPtr = firstHandler; asyncPtr != NULL;
|
---|
188 | asyncPtr = asyncPtr->nextPtr) {
|
---|
189 | if (asyncPtr->ready) {
|
---|
190 | break;
|
---|
191 | }
|
---|
192 | }
|
---|
193 | if (asyncPtr == NULL) {
|
---|
194 | break;
|
---|
195 | }
|
---|
196 | asyncPtr->ready = 0;
|
---|
197 | code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
|
---|
198 | }
|
---|
199 | asyncActive = 0;
|
---|
200 | return code;
|
---|
201 | }
|
---|
202 | |
---|
203 |
|
---|
204 | /*
|
---|
205 | *----------------------------------------------------------------------
|
---|
206 | *
|
---|
207 | * Tcl_AsyncDelete --
|
---|
208 | *
|
---|
209 | * Frees up all the state for an asynchronous handler. The handler
|
---|
210 | * should never be used again.
|
---|
211 | *
|
---|
212 | * Results:
|
---|
213 | * None.
|
---|
214 | *
|
---|
215 | * Side effects:
|
---|
216 | * The state associated with the handler is deleted.
|
---|
217 | *
|
---|
218 | *----------------------------------------------------------------------
|
---|
219 | */
|
---|
220 |
|
---|
221 | void
|
---|
222 | Tcl_AsyncDelete(async)
|
---|
223 | Tcl_AsyncHandler async; /* Token for handler to delete. */
|
---|
224 | {
|
---|
225 | AsyncHandler *asyncPtr = (AsyncHandler *) async;
|
---|
226 | AsyncHandler *prevPtr;
|
---|
227 |
|
---|
228 | if (firstHandler == asyncPtr) {
|
---|
229 | firstHandler = asyncPtr->nextPtr;
|
---|
230 | if (firstHandler == NULL) {
|
---|
231 | lastHandler = NULL;
|
---|
232 | }
|
---|
233 | } else {
|
---|
234 | prevPtr = firstHandler;
|
---|
235 | while (prevPtr->nextPtr != asyncPtr) {
|
---|
236 | prevPtr = prevPtr->nextPtr;
|
---|
237 | }
|
---|
238 | prevPtr->nextPtr = asyncPtr->nextPtr;
|
---|
239 | if (lastHandler == asyncPtr) {
|
---|
240 | lastHandler = prevPtr;
|
---|
241 | }
|
---|
242 | }
|
---|
243 | ckfree((char *) asyncPtr);
|
---|
244 | }
|
---|
245 | |
---|
246 |
|
---|
247 | /*
|
---|
248 | *----------------------------------------------------------------------
|
---|
249 | *
|
---|
250 | * Tcl_AsyncReady --
|
---|
251 | *
|
---|
252 | * This procedure can be used to tell whether Tcl_AsyncInvoke
|
---|
253 | * needs to be called. This procedure is the external interface
|
---|
254 | * for checking the internal asyncReady variable.
|
---|
255 | *
|
---|
256 | * Results:
|
---|
257 | * The return value is 1 whenever a handler is ready and is 0
|
---|
258 | * when no handlers are ready.
|
---|
259 | *
|
---|
260 | * Side effects:
|
---|
261 | * None.
|
---|
262 | *
|
---|
263 | *----------------------------------------------------------------------
|
---|
264 | */
|
---|
265 |
|
---|
266 | int
|
---|
267 | Tcl_AsyncReady()
|
---|
268 | {
|
---|
269 | return asyncReady;
|
---|
270 | }
|
---|