Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtdocbook / tcl / tclAsync.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: tclAsync.c /main/2 1996/08/08 14:42:49 cde-hp $ */
24 /* 
25  * tclAsync.c --
26  *
27  *      This file provides low-level support needed to invoke signal
28  *      handlers in a safe way.  The code here doesn't actually handle
29  *      signals, though.  This code is based on proposals made by
30  *      Mark Diekhans and Don Libes.
31  *
32  * Copyright (c) 1993 The Regents of the University of California.
33  * Copyright (c) 1994 Sun Microsystems, Inc.
34  *
35  * See the file "license.terms" for information on usage and redistribution
36  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
37  *
38  * SCCS: @(#) tclAsync.c 1.6 96/02/15 11:46:15
39  */
40
41 #include "tclInt.h"
42
43 /*
44  * One of the following structures exists for each asynchronous
45  * handler:
46  */
47
48 typedef struct AsyncHandler {
49     int ready;                          /* Non-zero means this handler should
50                                          * be invoked in the next call to
51                                          * Tcl_AsyncInvoke. */
52     struct AsyncHandler *nextPtr;       /* Next in list of all handlers for
53                                          * the process. */
54     Tcl_AsyncProc *proc;                /* Procedure to call when handler
55                                          * is invoked. */
56     ClientData clientData;              /* Value to pass to handler when it
57                                          * is invoked. */
58 } AsyncHandler;
59
60 /*
61  * The variables below maintain a list of all existing handlers.
62  */
63
64 static AsyncHandler *firstHandler;      /* First handler defined for process,
65                                          * or NULL if none. */
66 static AsyncHandler *lastHandler;       /* Last handler or NULL. */
67
68 /*
69  * The variable below is set to 1 whenever a handler becomes ready and
70  * it is cleared to zero whenever Tcl_AsyncInvoke is called.  It can be
71  * checked elsewhere in the application by calling Tcl_AsyncReady to see
72  * if Tcl_AsyncInvoke should be invoked.
73  */
74
75 static int asyncReady = 0;
76
77 /*
78  * The variable below indicates whether Tcl_AsyncInvoke is currently
79  * working.  If so then we won't set asyncReady again until
80  * Tcl_AsyncInvoke returns.
81  */
82
83 static int asyncActive = 0;
84 \f
85 /*
86  *----------------------------------------------------------------------
87  *
88  * Tcl_AsyncCreate --
89  *
90  *      This procedure creates the data structures for an asynchronous
91  *      handler, so that no memory has to be allocated when the handler
92  *      is activated.
93  *
94  * Results:
95  *      The return value is a token for the handler, which can be used
96  *      to activate it later on.
97  *
98  * Side effects:
99  *      Information about the handler is recorded.
100  *
101  *----------------------------------------------------------------------
102  */
103
104 Tcl_AsyncHandler
105 Tcl_AsyncCreate(proc, clientData)
106     Tcl_AsyncProc *proc;                /* Procedure to call when handler
107                                          * is invoked. */
108     ClientData clientData;              /* Argument to pass to handler. */
109 {
110     AsyncHandler *asyncPtr;
111
112     asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
113     asyncPtr->ready = 0;
114     asyncPtr->nextPtr = NULL;
115     asyncPtr->proc = proc;
116     asyncPtr->clientData = clientData;
117     if (firstHandler == NULL) {
118         firstHandler = asyncPtr;
119     } else {
120         lastHandler->nextPtr = asyncPtr;
121     }
122     lastHandler = asyncPtr;
123     return (Tcl_AsyncHandler) asyncPtr;
124 }
125 \f
126 /*
127  *----------------------------------------------------------------------
128  *
129  * Tcl_AsyncMark --
130  *
131  *      This procedure is called to request that an asynchronous handler
132  *      be invoked as soon as possible.  It's typically called from
133  *      an interrupt handler, where it isn't safe to do anything that
134  *      depends on or modifies application state.
135  *
136  * Results:
137  *      None.
138  *
139  * Side effects:
140  *      The handler gets marked for invocation later.
141  *
142  *----------------------------------------------------------------------
143  */
144
145 void
146 Tcl_AsyncMark(async)
147     Tcl_AsyncHandler async;             /* Token for handler. */
148 {
149     ((AsyncHandler *) async)->ready = 1;
150     if (!asyncActive) {
151         asyncReady = 1;
152     }
153 }
154 \f
155 /*
156  *----------------------------------------------------------------------
157  *
158  * Tcl_AsyncInvoke --
159  *
160  *      This procedure is called at a "safe" time at background level
161  *      to invoke any active asynchronous handlers.
162  *
163  * Results:
164  *      The return value is a normal Tcl result, which is intended to
165  *      replace the code argument as the current completion code for
166  *      interp.
167  *
168  * Side effects:
169  *      Depends on the handlers that are active.
170  *
171  *----------------------------------------------------------------------
172  */
173
174 int
175 Tcl_AsyncInvoke(interp, code)
176     Tcl_Interp *interp;                 /* If invoked from Tcl_Eval just after
177                                          * completing a command, points to
178                                          * interpreter.  Otherwise it is
179                                          * NULL. */
180     int code;                           /* If interp is non-NULL, this gives
181                                          * completion code from command that
182                                          * just completed. */
183 {
184     AsyncHandler *asyncPtr;
185
186     if (asyncReady == 0) {
187         return code;
188     }
189     asyncReady = 0;
190     asyncActive = 1;
191     if (interp == NULL) {
192         code = 0;
193     }
194
195     /*
196      * Make one or more passes over the list of handlers, invoking
197      * at most one handler in each pass.  After invoking a handler,
198      * go back to the start of the list again so that (a) if a new
199      * higher-priority handler gets marked while executing a lower
200      * priority handler, we execute the higher-priority handler
201      * next, and (b) if a handler gets deleted during the execution
202      * of a handler, then the list structure may change so it isn't
203      * safe to continue down the list anyway.
204      */
205
206     while (1) {
207         for (asyncPtr = firstHandler; asyncPtr != NULL;
208                 asyncPtr = asyncPtr->nextPtr) {
209             if (asyncPtr->ready) {
210                 break;
211             }
212         }
213         if (asyncPtr == NULL) {
214             break;
215         }
216         asyncPtr->ready = 0;
217         code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
218     }
219     asyncActive = 0;
220     return code;
221 }
222 \f
223 /*
224  *----------------------------------------------------------------------
225  *
226  * Tcl_AsyncDelete --
227  *
228  *      Frees up all the state for an asynchronous handler.  The handler
229  *      should never be used again.
230  *
231  * Results:
232  *      None.
233  *
234  * Side effects:
235  *      The state associated with the handler is deleted.
236  *
237  *----------------------------------------------------------------------
238  */
239
240 void
241 Tcl_AsyncDelete(async)
242     Tcl_AsyncHandler async;             /* Token for handler to delete. */
243 {
244     AsyncHandler *asyncPtr = (AsyncHandler *) async;
245     AsyncHandler *prevPtr;
246
247     if (firstHandler == asyncPtr) {
248         firstHandler = asyncPtr->nextPtr;
249         if (firstHandler == NULL) {
250             lastHandler = NULL;
251         }
252     } else {
253         prevPtr = firstHandler;
254         while (prevPtr->nextPtr != asyncPtr) {
255             prevPtr = prevPtr->nextPtr;
256         }
257         prevPtr->nextPtr = asyncPtr->nextPtr;
258         if (lastHandler == asyncPtr) {
259             lastHandler = prevPtr;
260         }
261     }
262     ckfree((char *) asyncPtr);
263 }
264 \f
265 /*
266  *----------------------------------------------------------------------
267  *
268  * Tcl_AsyncReady --
269  *
270  *      This procedure can be used to tell whether Tcl_AsyncInvoke
271  *      needs to be called.  This procedure is the external interface
272  *      for checking the internal asyncReady variable.
273  *
274  * Results:
275  *      The return value is 1 whenever a handler is ready and is 0
276  *      when no handlers are ready.
277  *
278  * Side effects:
279  *      None.
280  *
281  *----------------------------------------------------------------------
282  */
283
284 int
285 Tcl_AsyncReady()
286 {
287     return asyncReady;
288 }