source: trunk/tcl/tclAsync.c@ 7

Last change on this file since 7 was 2, checked in by Pavel Demin, 16 years ago

first commit

File size: 6.8 KB
Line 
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
25typedef 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
41static AsyncHandler *firstHandler; /* First handler defined for process,
42 * or NULL if none. */
43static 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
52static 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
60static 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
82Tcl_AsyncHandler
83Tcl_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
124void
125Tcl_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
154int
155Tcl_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
221void
222Tcl_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
266int
267Tcl_AsyncReady()
268{
269 return asyncReady;
270}
Note: See TracBrowser for help on using the repository browser.