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