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