home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tcl / os2 / tclAsync.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  7KB  |  266 lines

  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.