/*1* tclAsync.c --2*3* This file provides low-level support needed to invoke signal4* handlers in a safe way. The code here doesn't actually handle5* signals, though. This code is based on proposals made by6* Mark Diekhans and Don Libes.7*8* Copyright (c) 1993 The Regents of the University of California.9* Copyright (c) 1994 Sun Microsystems, Inc.10*11* See the file "license.terms" for information on usage and redistribution12* of this file, and for a DISCLAIMER OF ALL WARRANTIES.13*14* SCCS: @(#) tclAsync.c 1.6 96/02/15 11:46:1515*/1617#include "tclInt.h"1819/*20* One of the following structures exists for each asynchronous21* handler:22*/2324typedef struct AsyncHandler {25int ready; /* Non-zero means this handler should26* be invoked in the next call to27* Tcl_AsyncInvoke. */28struct AsyncHandler *nextPtr; /* Next in list of all handlers for29* the process. */30Tcl_AsyncProc *proc; /* Procedure to call when handler31* is invoked. */32ClientData clientData; /* Value to pass to handler when it33* is invoked. */34} AsyncHandler;3536/*37* The variables below maintain a list of all existing handlers.38*/3940static AsyncHandler *firstHandler; /* First handler defined for process,41* or NULL if none. */42static AsyncHandler *lastHandler; /* Last handler or NULL. */4344/*45* The variable below is set to 1 whenever a handler becomes ready and46* it is cleared to zero whenever Tcl_AsyncInvoke is called. It can be47* checked elsewhere in the application by calling Tcl_AsyncReady to see48* if Tcl_AsyncInvoke should be invoked.49*/5051static int asyncReady = 0;5253/*54* The variable below indicates whether Tcl_AsyncInvoke is currently55* working. If so then we won't set asyncReady again until56* Tcl_AsyncInvoke returns.57*/5859static int asyncActive = 0;6061/*62*----------------------------------------------------------------------63*64* Tcl_AsyncCreate --65*66* This procedure creates the data structures for an asynchronous67* handler, so that no memory has to be allocated when the handler68* is activated.69*70* Results:71* The return value is a token for the handler, which can be used72* to activate it later on.73*74* Side effects:75* Information about the handler is recorded.76*77*----------------------------------------------------------------------78*/7980Tcl_AsyncHandler81Tcl_AsyncCreate(proc, clientData)82Tcl_AsyncProc *proc; /* Procedure to call when handler83* is invoked. */84ClientData clientData; /* Argument to pass to handler. */85{86AsyncHandler *asyncPtr;8788asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));89asyncPtr->ready = 0;90asyncPtr->nextPtr = NULL;91asyncPtr->proc = proc;92asyncPtr->clientData = clientData;93if (firstHandler == NULL) {94firstHandler = asyncPtr;95} else {96lastHandler->nextPtr = asyncPtr;97}98lastHandler = asyncPtr;99return (Tcl_AsyncHandler) asyncPtr;100}101102/*103*----------------------------------------------------------------------104*105* Tcl_AsyncMark --106*107* This procedure is called to request that an asynchronous handler108* be invoked as soon as possible. It's typically called from109* an interrupt handler, where it isn't safe to do anything that110* depends on or modifies application state.111*112* Results:113* None.114*115* Side effects:116* The handler gets marked for invocation later.117*118*----------------------------------------------------------------------119*/120121void122Tcl_AsyncMark(async)123Tcl_AsyncHandler async; /* Token for handler. */124{125((AsyncHandler *) async)->ready = 1;126if (!asyncActive) {127asyncReady = 1;128}129}130131/*132*----------------------------------------------------------------------133*134* Tcl_AsyncInvoke --135*136* This procedure is called at a "safe" time at background level137* to invoke any active asynchronous handlers.138*139* Results:140* The return value is a normal Tcl result, which is intended to141* replace the code argument as the current completion code for142* interp.143*144* Side effects:145* Depends on the handlers that are active.146*147*----------------------------------------------------------------------148*/149150int151Tcl_AsyncInvoke(interp, code)152Tcl_Interp *interp; /* If invoked from Tcl_Eval just after153* completing a command, points to154* interpreter. Otherwise it is155* NULL. */156int code; /* If interp is non-NULL, this gives157* completion code from command that158* just completed. */159{160AsyncHandler *asyncPtr;161162if (asyncReady == 0) {163return code;164}165asyncReady = 0;166asyncActive = 1;167if (interp == NULL) {168code = 0;169}170171/*172* Make one or more passes over the list of handlers, invoking173* at most one handler in each pass. After invoking a handler,174* go back to the start of the list again so that (a) if a new175* higher-priority handler gets marked while executing a lower176* priority handler, we execute the higher-priority handler177* next, and (b) if a handler gets deleted during the execution178* of a handler, then the list structure may change so it isn't179* safe to continue down the list anyway.180*/181182while (1) {183for (asyncPtr = firstHandler; asyncPtr != NULL;184asyncPtr = asyncPtr->nextPtr) {185if (asyncPtr->ready) {186break;187}188}189if (asyncPtr == NULL) {190break;191}192asyncPtr->ready = 0;193code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);194}195asyncActive = 0;196return code;197}198199/*200*----------------------------------------------------------------------201*202* Tcl_AsyncDelete --203*204* Frees up all the state for an asynchronous handler. The handler205* should never be used again.206*207* Results:208* None.209*210* Side effects:211* The state associated with the handler is deleted.212*213*----------------------------------------------------------------------214*/215216void217Tcl_AsyncDelete(async)218Tcl_AsyncHandler async; /* Token for handler to delete. */219{220AsyncHandler *asyncPtr = (AsyncHandler *) async;221AsyncHandler *prevPtr;222223if (firstHandler == asyncPtr) {224firstHandler = asyncPtr->nextPtr;225if (firstHandler == NULL) {226lastHandler = NULL;227}228} else {229prevPtr = firstHandler;230while (prevPtr->nextPtr != asyncPtr) {231prevPtr = prevPtr->nextPtr;232}233prevPtr->nextPtr = asyncPtr->nextPtr;234if (lastHandler == asyncPtr) {235lastHandler = prevPtr;236}237}238ckfree((char *) asyncPtr);239}240241/*242*----------------------------------------------------------------------243*244* Tcl_AsyncReady --245*246* This procedure can be used to tell whether Tcl_AsyncInvoke247* needs to be called. This procedure is the external interface248* for checking the internal asyncReady variable.249*250* Results:251* The return value is 1 whenever a handler is ready and is 0252* when no handlers are ready.253*254* Side effects:255* None.256*257*----------------------------------------------------------------------258*/259260int261Tcl_AsyncReady()262{263return asyncReady;264}265266267