tclAsync.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:9k
源码类别:

通讯编程

开发平台:

Visual C++

  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.6.12.1 2006/07/11 13:18:10 vasiljevic Exp $
  16.  */
  17. #include "tclInt.h"
  18. #include "tclPort.h"
  19. /* Forward declaration */
  20. struct ThreadSpecificData;
  21. /*
  22.  * One of the following structures exists for each asynchronous
  23.  * handler:
  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.     struct ThreadSpecificData *originTsd;
  36. /* Used in Tcl_AsyncMark to modify thread-
  37.  * specific data from outside the thread
  38.  * it is associated to. */
  39.     Tcl_ThreadId originThrdId; /* Origin thread where this token was
  40.  * created and where it will be
  41.  * yielded. */
  42. } AsyncHandler;
  43. typedef struct ThreadSpecificData {
  44.     /*
  45.      * The variables below maintain a list of all existing handlers
  46.      * specific to the calling thread.
  47.      */
  48.     AsyncHandler *firstHandler;     /* First handler defined for process,
  49.      * or NULL if none. */
  50.     AsyncHandler *lastHandler;     /* Last handler or NULL. */
  51.     /*
  52.      * The variable below is set to 1 whenever a handler becomes ready and
  53.      * it is cleared to zero whenever Tcl_AsyncInvoke is called.  It can be
  54.      * checked elsewhere in the application by calling Tcl_AsyncReady to see
  55.      * if Tcl_AsyncInvoke should be invoked.
  56.      */
  57.     int asyncReady;
  58.     /*
  59.      * The variable below indicates whether Tcl_AsyncInvoke is currently
  60.      * working.  If so then we won't set asyncReady again until
  61.      * Tcl_AsyncInvoke returns.
  62.      */
  63.     int asyncActive;
  64.     Tcl_Mutex asyncMutex;   /* Thread-specific AsyncHandler linked-list lock */
  65. } ThreadSpecificData;
  66. static Tcl_ThreadDataKey dataKey;
  67. /*
  68.  *----------------------------------------------------------------------
  69.  *
  70.  * TclFinalizeAsync --
  71.  *
  72.  * Finalizes the mutex in the thread local data structure for the
  73.  * async subsystem.
  74.  *
  75.  * Results:
  76.  * None.
  77.  *
  78.  * Side effects:
  79.  * Forgets knowledge of the mutex should it have been created.
  80.  *
  81.  *----------------------------------------------------------------------
  82.  */
  83. void
  84. TclFinalizeAsync()
  85. {
  86.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  87.     if (tsdPtr->asyncMutex != NULL) {
  88. Tcl_MutexFinalize(&tsdPtr->asyncMutex);
  89.     }
  90. }
  91. /*
  92.  *----------------------------------------------------------------------
  93.  *
  94.  * Tcl_AsyncCreate --
  95.  *
  96.  * This procedure creates the data structures for an asynchronous
  97.  * handler, so that no memory has to be allocated when the handler
  98.  * is activated.
  99.  *
  100.  * Results:
  101.  * The return value is a token for the handler, which can be used
  102.  * to activate it later on.
  103.  *
  104.  * Side effects:
  105.  * Information about the handler is recorded.
  106.  *
  107.  *----------------------------------------------------------------------
  108.  */
  109. Tcl_AsyncHandler
  110. Tcl_AsyncCreate(proc, clientData)
  111.     Tcl_AsyncProc *proc; /* Procedure to call when handler
  112.  * is invoked. */
  113.     ClientData clientData; /* Argument to pass to handler. */
  114. {
  115.     AsyncHandler *asyncPtr;
  116.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  117.     asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
  118.     asyncPtr->ready = 0;
  119.     asyncPtr->nextPtr = NULL;
  120.     asyncPtr->proc = proc;
  121.     asyncPtr->clientData = clientData;
  122.     asyncPtr->originTsd = tsdPtr;
  123.     asyncPtr->originThrdId = Tcl_GetCurrentThread();
  124.     Tcl_MutexLock(&tsdPtr->asyncMutex);
  125.     if (tsdPtr->firstHandler == NULL) {
  126. tsdPtr->firstHandler = asyncPtr;
  127.     } else {
  128. tsdPtr->lastHandler->nextPtr = asyncPtr;
  129.     }
  130.     tsdPtr->lastHandler = asyncPtr;
  131.     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
  132.     return (Tcl_AsyncHandler) asyncPtr;
  133. }
  134. /*
  135.  *----------------------------------------------------------------------
  136.  *
  137.  * Tcl_AsyncMark --
  138.  *
  139.  * This procedure is called to request that an asynchronous handler
  140.  * be invoked as soon as possible.  It's typically called from
  141.  * an interrupt handler, where it isn't safe to do anything that
  142.  * depends on or modifies application state.
  143.  *
  144.  * Results:
  145.  * None.
  146.  *
  147.  * Side effects:
  148.  * The handler gets marked for invocation later.
  149.  *
  150.  *----------------------------------------------------------------------
  151.  */
  152. void
  153. Tcl_AsyncMark(async)
  154.     Tcl_AsyncHandler async; /* Token for handler. */
  155. {
  156.     AsyncHandler *token = (AsyncHandler *) async;
  157.     Tcl_MutexLock(&token->originTsd->asyncMutex);
  158.     token->ready = 1;
  159.     if (!token->originTsd->asyncActive) {
  160. token->originTsd->asyncReady = 1;
  161. Tcl_ThreadAlert(token->originThrdId);
  162.     }
  163.     Tcl_MutexUnlock(&token->originTsd->asyncMutex);
  164. }
  165. /*
  166.  *----------------------------------------------------------------------
  167.  *
  168.  * Tcl_AsyncInvoke --
  169.  *
  170.  * This procedure is called at a "safe" time at background level
  171.  * to invoke any active asynchronous handlers.
  172.  *
  173.  * Results:
  174.  * The return value is a normal Tcl result, which is intended to
  175.  * replace the code argument as the current completion code for
  176.  * interp.
  177.  *
  178.  * Side effects:
  179.  * Depends on the handlers that are active.
  180.  *
  181.  *----------------------------------------------------------------------
  182.  */
  183. int
  184. Tcl_AsyncInvoke(interp, code)
  185.     Tcl_Interp *interp; /* If invoked from Tcl_Eval just after
  186.  * completing a command, points to
  187.  * interpreter.  Otherwise it is
  188.  * NULL. */
  189.     int code;  /* If interp is non-NULL, this gives
  190.  * completion code from command that
  191.  * just completed. */
  192. {
  193.     AsyncHandler *asyncPtr;
  194.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  195.     Tcl_MutexLock(&tsdPtr->asyncMutex);
  196.     if (tsdPtr->asyncReady == 0) {
  197. Tcl_MutexUnlock(&tsdPtr->asyncMutex);
  198. return code;
  199.     }
  200.     tsdPtr->asyncReady = 0;
  201.     tsdPtr->asyncActive = 1;
  202.     if (interp == NULL) {
  203. code = 0;
  204.     }
  205.     /*
  206.      * Make one or more passes over the list of handlers, invoking
  207.      * at most one handler in each pass.  After invoking a handler,
  208.      * go back to the start of the list again so that (a) if a new
  209.      * higher-priority handler gets marked while executing a lower
  210.      * priority handler, we execute the higher-priority handler
  211.      * next, and (b) if a handler gets deleted during the execution
  212.      * of a handler, then the list structure may change so it isn't
  213.      * safe to continue down the list anyway.
  214.      */
  215.     while (1) {
  216. for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL;
  217. asyncPtr = asyncPtr->nextPtr) {
  218.     if (asyncPtr->ready) {
  219. break;
  220.     }
  221. }
  222. if (asyncPtr == NULL) {
  223.     break;
  224. }
  225. asyncPtr->ready = 0;
  226. Tcl_MutexUnlock(&tsdPtr->asyncMutex);
  227. code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
  228. Tcl_MutexLock(&tsdPtr->asyncMutex);
  229.     }
  230.     tsdPtr->asyncActive = 0;
  231.     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
  232.     return code;
  233. }
  234. /*
  235.  *----------------------------------------------------------------------
  236.  *
  237.  * Tcl_AsyncDelete --
  238.  *
  239.  * Frees up all the state for an asynchronous handler.  The handler
  240.  * should never be used again.
  241.  *
  242.  * Results:
  243.  * None.
  244.  *
  245.  * Side effects:
  246.  * The state associated with the handler is deleted.
  247.  *
  248.  *----------------------------------------------------------------------
  249.  */
  250. void
  251. Tcl_AsyncDelete(async)
  252.     Tcl_AsyncHandler async; /* Token for handler to delete. */
  253. {
  254.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  255.     AsyncHandler *asyncPtr = (AsyncHandler *) async;
  256.     AsyncHandler *prevPtr;
  257.     /*
  258.      * Conservatively check the existence of the linked list of
  259.      * registered handlers, as we may come at this point even
  260.      * when the TSD's for the current thread have been already
  261.      * garbage-collected.
  262.      */
  263.     Tcl_MutexLock(&tsdPtr->asyncMutex);
  264.     if (tsdPtr->firstHandler != NULL ) {
  265. if (tsdPtr->firstHandler == asyncPtr) {
  266.     tsdPtr->firstHandler = asyncPtr->nextPtr;
  267.     if (tsdPtr->firstHandler == NULL) {
  268. tsdPtr->lastHandler = NULL;
  269.     }
  270. } else {
  271.     prevPtr = tsdPtr->firstHandler;
  272.     while (prevPtr->nextPtr != asyncPtr) {
  273. prevPtr = prevPtr->nextPtr;
  274.     }
  275.     prevPtr->nextPtr = asyncPtr->nextPtr;
  276.     if (tsdPtr->lastHandler == asyncPtr) {
  277. tsdPtr->lastHandler = prevPtr;
  278.     }
  279. }
  280.     }
  281.     Tcl_MutexUnlock(&tsdPtr->asyncMutex);
  282.     ckfree((char *) asyncPtr);
  283. }
  284. /*
  285.  *----------------------------------------------------------------------
  286.  *
  287.  * Tcl_AsyncReady --
  288.  *
  289.  * This procedure can be used to tell whether Tcl_AsyncInvoke
  290.  * needs to be called.  This procedure is the external interface
  291.  * for checking the thread-specific asyncReady variable.
  292.  *
  293.  * Results:
  294.  *  The return value is 1 whenever a handler is ready and is 0
  295.  * when no handlers are ready.
  296.  *
  297.  * Side effects:
  298.  * None.
  299.  *
  300.  *----------------------------------------------------------------------
  301.  */
  302. int
  303. Tcl_AsyncReady()
  304. {
  305.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  306.     return tsdPtr->asyncReady;
  307. }