Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclAsync.c
1810 views
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
* SCCS: @(#) tclAsync.c 1.6 96/02/15 11:46:15
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
* Tcl_AsyncCreate --
66
*
67
* This procedure creates the data structures for an asynchronous
68
* handler, so that no memory has to be allocated when the handler
69
* is activated.
70
*
71
* Results:
72
* The return value is a token for the handler, which can be used
73
* to activate it later on.
74
*
75
* Side effects:
76
* Information about the handler is recorded.
77
*
78
*----------------------------------------------------------------------
79
*/
80
81
Tcl_AsyncHandler
82
Tcl_AsyncCreate(proc, clientData)
83
Tcl_AsyncProc *proc; /* Procedure to call when handler
84
* is invoked. */
85
ClientData clientData; /* Argument to pass to handler. */
86
{
87
AsyncHandler *asyncPtr;
88
89
asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
90
asyncPtr->ready = 0;
91
asyncPtr->nextPtr = NULL;
92
asyncPtr->proc = proc;
93
asyncPtr->clientData = clientData;
94
if (firstHandler == NULL) {
95
firstHandler = asyncPtr;
96
} else {
97
lastHandler->nextPtr = asyncPtr;
98
}
99
lastHandler = asyncPtr;
100
return (Tcl_AsyncHandler) asyncPtr;
101
}
102
103
/*
104
*----------------------------------------------------------------------
105
*
106
* Tcl_AsyncMark --
107
*
108
* This procedure is called to request that an asynchronous handler
109
* be invoked as soon as possible. It's typically called from
110
* an interrupt handler, where it isn't safe to do anything that
111
* depends on or modifies application state.
112
*
113
* Results:
114
* None.
115
*
116
* Side effects:
117
* The handler gets marked for invocation later.
118
*
119
*----------------------------------------------------------------------
120
*/
121
122
void
123
Tcl_AsyncMark(async)
124
Tcl_AsyncHandler async; /* Token for handler. */
125
{
126
((AsyncHandler *) async)->ready = 1;
127
if (!asyncActive) {
128
asyncReady = 1;
129
}
130
}
131
132
/*
133
*----------------------------------------------------------------------
134
*
135
* Tcl_AsyncInvoke --
136
*
137
* This procedure is called at a "safe" time at background level
138
* to invoke any active asynchronous handlers.
139
*
140
* Results:
141
* The return value is a normal Tcl result, which is intended to
142
* replace the code argument as the current completion code for
143
* interp.
144
*
145
* Side effects:
146
* Depends on the handlers that are active.
147
*
148
*----------------------------------------------------------------------
149
*/
150
151
int
152
Tcl_AsyncInvoke(interp, code)
153
Tcl_Interp *interp; /* If invoked from Tcl_Eval just after
154
* completing a command, points to
155
* interpreter. Otherwise it is
156
* NULL. */
157
int code; /* If interp is non-NULL, this gives
158
* completion code from command that
159
* just completed. */
160
{
161
AsyncHandler *asyncPtr;
162
163
if (asyncReady == 0) {
164
return code;
165
}
166
asyncReady = 0;
167
asyncActive = 1;
168
if (interp == NULL) {
169
code = 0;
170
}
171
172
/*
173
* Make one or more passes over the list of handlers, invoking
174
* at most one handler in each pass. After invoking a handler,
175
* go back to the start of the list again so that (a) if a new
176
* higher-priority handler gets marked while executing a lower
177
* priority handler, we execute the higher-priority handler
178
* next, and (b) if a handler gets deleted during the execution
179
* of a handler, then the list structure may change so it isn't
180
* safe to continue down the list anyway.
181
*/
182
183
while (1) {
184
for (asyncPtr = firstHandler; asyncPtr != NULL;
185
asyncPtr = asyncPtr->nextPtr) {
186
if (asyncPtr->ready) {
187
break;
188
}
189
}
190
if (asyncPtr == NULL) {
191
break;
192
}
193
asyncPtr->ready = 0;
194
code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
195
}
196
asyncActive = 0;
197
return code;
198
}
199
200
/*
201
*----------------------------------------------------------------------
202
*
203
* Tcl_AsyncDelete --
204
*
205
* Frees up all the state for an asynchronous handler. The handler
206
* should never be used again.
207
*
208
* Results:
209
* None.
210
*
211
* Side effects:
212
* The state associated with the handler is deleted.
213
*
214
*----------------------------------------------------------------------
215
*/
216
217
void
218
Tcl_AsyncDelete(async)
219
Tcl_AsyncHandler async; /* Token for handler to delete. */
220
{
221
AsyncHandler *asyncPtr = (AsyncHandler *) async;
222
AsyncHandler *prevPtr;
223
224
if (firstHandler == asyncPtr) {
225
firstHandler = asyncPtr->nextPtr;
226
if (firstHandler == NULL) {
227
lastHandler = NULL;
228
}
229
} else {
230
prevPtr = firstHandler;
231
while (prevPtr->nextPtr != asyncPtr) {
232
prevPtr = prevPtr->nextPtr;
233
}
234
prevPtr->nextPtr = asyncPtr->nextPtr;
235
if (lastHandler == asyncPtr) {
236
lastHandler = prevPtr;
237
}
238
}
239
ckfree((char *) asyncPtr);
240
}
241
242
/*
243
*----------------------------------------------------------------------
244
*
245
* Tcl_AsyncReady --
246
*
247
* This procedure can be used to tell whether Tcl_AsyncInvoke
248
* needs to be called. This procedure is the external interface
249
* for checking the internal asyncReady variable.
250
*
251
* Results:
252
* The return value is 1 whenever a handler is ready and is 0
253
* when no handlers are ready.
254
*
255
* Side effects:
256
* None.
257
*
258
*----------------------------------------------------------------------
259
*/
260
261
int
262
Tcl_AsyncReady()
263
{
264
return asyncReady;
265
}
266
267