os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclNotify.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclNotify.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1127 @@
1.4 +/*
1.5 + * tclNotify.c --
1.6 + *
1.7 + * This file implements the generic portion of the Tcl notifier.
1.8 + * The notifier is lowest-level part of the event system. It
1.9 + * manages an event queue that holds Tcl_Event structures. The
1.10 + * platform specific portion of the notifier is defined in the
1.11 + * tcl*Notify.c files in each platform directory.
1.12 + *
1.13 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.14 + * Copyright (c) 1998 by Scriptics Corporation.
1.15 + * Copyright (c) 2003 by Kevin B. Kenny. All rights reserved.
1.16 + * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
1.17 + *
1.18 + * See the file "license.terms" for information on usage and redistribution
1.19 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.20 + *
1.21 + * RCS: @(#) $Id: tclNotify.c,v 1.11.2.2 2005/04/26 00:46:02 das Exp $
1.22 + */
1.23 +
1.24 +#include "tclInt.h"
1.25 +#include "tclPort.h"
1.26 +#if defined(__SYMBIAN32__) && defined(__WINSCW__)
1.27 +#include "tclSymbianGlobals.h"
1.28 +#define dataKey getdataKey(5)
1.29 +#endif
1.30 +
1.31 +extern TclStubs tclStubs;
1.32 +
1.33 +/*
1.34 + * For each event source (created with Tcl_CreateEventSource) there
1.35 + * is a structure of the following type:
1.36 + */
1.37 +
1.38 +typedef struct EventSource {
1.39 + Tcl_EventSetupProc *setupProc;
1.40 + Tcl_EventCheckProc *checkProc;
1.41 + ClientData clientData;
1.42 + struct EventSource *nextPtr;
1.43 +} EventSource;
1.44 +
1.45 +/*
1.46 + * The following structure keeps track of the state of the notifier on a
1.47 + * per-thread basis. The first three elements keep track of the event queue.
1.48 + * In addition to the first (next to be serviced) and last events in the queue,
1.49 + * we keep track of a "marker" event. This provides a simple priority
1.50 + * mechanism whereby events can be inserted at the front of the queue but
1.51 + * behind all other high-priority events already in the queue (this is used for
1.52 + * things like a sequence of Enter and Leave events generated during a grab in
1.53 + * Tk). These elements are protected by the queueMutex so that any thread
1.54 + * can queue an event on any notifier. Note that all of the values in this
1.55 + * structure will be initialized to 0.
1.56 + */
1.57 +
1.58 +typedef struct ThreadSpecificData {
1.59 + Tcl_Event *firstEventPtr; /* First pending event, or NULL if none. */
1.60 + Tcl_Event *lastEventPtr; /* Last pending event, or NULL if none. */
1.61 + Tcl_Event *markerEventPtr; /* Last high-priority event in queue, or
1.62 + * NULL if none. */
1.63 + Tcl_Mutex queueMutex; /* Mutex to protect access to the previous
1.64 + * three fields. */
1.65 + int serviceMode; /* One of TCL_SERVICE_NONE or
1.66 + * TCL_SERVICE_ALL. */
1.67 + int blockTimeSet; /* 0 means there is no maximum block
1.68 + * time: block forever. */
1.69 + Tcl_Time blockTime; /* If blockTimeSet is 1, gives the
1.70 + * maximum elapsed time for the next block. */
1.71 + int inTraversal; /* 1 if Tcl_SetMaxBlockTime is being
1.72 + * called during an event source traversal. */
1.73 + EventSource *firstEventSourcePtr;
1.74 + /* Pointer to first event source in
1.75 + * list of event sources for this thread. */
1.76 + Tcl_ThreadId threadId; /* Thread that owns this notifier instance. */
1.77 + ClientData clientData; /* Opaque handle for platform specific
1.78 + * notifier. */
1.79 + int initialized; /* 1 if notifier has been initialized. */
1.80 + struct ThreadSpecificData *nextPtr;
1.81 + /* Next notifier in global list of notifiers.
1.82 + * Access is controlled by the listLock global
1.83 + * mutex. */
1.84 +} ThreadSpecificData;
1.85 +
1.86 +#if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
1.87 +static Tcl_ThreadDataKey dataKey;
1.88 +
1.89 +
1.90 +/*
1.91 + * Global list of notifiers. Access to this list is controlled by the
1.92 + * listLock mutex. If this becomes a performance bottleneck, this could
1.93 + * be replaced with a hashtable.
1.94 + */
1.95 +
1.96 +static ThreadSpecificData *firstNotifierPtr;
1.97 +#else
1.98 +#define firstNotifierPtr (*(ThreadSpecificData**)get_firstNotifierPtr())
1.99 +#endif
1.100 +TCL_DECLARE_MUTEX(listLock)
1.101 +
1.102 +/*
1.103 + * Declarations for routines used only in this file.
1.104 + */
1.105 +
1.106 +static void QueueEvent _ANSI_ARGS_((ThreadSpecificData *tsdPtr,
1.107 + Tcl_Event* evPtr, Tcl_QueuePosition position));
1.108 +
1.109 +/*
1.110 + *----------------------------------------------------------------------
1.111 + *
1.112 + * TclInitNotifier --
1.113 + *
1.114 + * Initialize the thread local data structures for the notifier
1.115 + * subsystem.
1.116 + *
1.117 + * Results:
1.118 + * None.
1.119 + *
1.120 + * Side effects:
1.121 + * Adds the current thread to the global list of notifiers.
1.122 + *
1.123 + *----------------------------------------------------------------------
1.124 + */
1.125 +
1.126 +void
1.127 +TclInitNotifier()
1.128 +{
1.129 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.130 +
1.131 + Tcl_MutexLock(&listLock);
1.132 +
1.133 + tsdPtr->threadId = Tcl_GetCurrentThread();
1.134 + tsdPtr->clientData = tclStubs.tcl_InitNotifier();
1.135 + tsdPtr->initialized = 1;
1.136 + tsdPtr->nextPtr = firstNotifierPtr;
1.137 + firstNotifierPtr = tsdPtr;
1.138 +
1.139 + Tcl_MutexUnlock(&listLock);
1.140 +}
1.141 +
1.142 +/*
1.143 + *----------------------------------------------------------------------
1.144 + *
1.145 + * TclFinalizeNotifier --
1.146 + *
1.147 + * Finalize the thread local data structures for the notifier
1.148 + * subsystem.
1.149 + *
1.150 + * Results:
1.151 + * None.
1.152 + *
1.153 + * Side effects:
1.154 + * Removes the notifier associated with the current thread from
1.155 + * the global notifier list. This is done only if the notifier
1.156 + * was initialized for this thread by call to TclInitNotifier().
1.157 + * This is always true for threads which have been seeded with
1.158 + * an Tcl interpreter, since the call to Tcl_CreateInterp will,
1.159 + * among other things, call TclInitializeSubsystems() and this
1.160 + * one will, in turn, call the TclInitNotifier() for the thread.
1.161 + * For threads created without the Tcl interpreter, though,
1.162 + * nobody is explicitly nor implicitly calling the TclInitNotifier
1.163 + * hence, TclFinalizeNotifier should not be performed at all.
1.164 + *
1.165 + *----------------------------------------------------------------------
1.166 + */
1.167 +
1.168 +void
1.169 +TclFinalizeNotifier()
1.170 +{
1.171 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.172 + ThreadSpecificData **prevPtrPtr;
1.173 + Tcl_Event *evPtr, *hold;
1.174 +
1.175 + if (!tsdPtr->initialized) {
1.176 + return; /* Notifier not initialized for the current thread */
1.177 + }
1.178 +
1.179 + Tcl_MutexLock(&(tsdPtr->queueMutex));
1.180 + for (evPtr = tsdPtr->firstEventPtr; evPtr != (Tcl_Event *) NULL; ) {
1.181 + hold = evPtr;
1.182 + evPtr = evPtr->nextPtr;
1.183 + ckfree((char *) hold);
1.184 + }
1.185 + tsdPtr->firstEventPtr = NULL;
1.186 + tsdPtr->lastEventPtr = NULL;
1.187 + Tcl_MutexUnlock(&(tsdPtr->queueMutex));
1.188 +
1.189 + Tcl_MutexLock(&listLock);
1.190 +
1.191 + if (tclStubs.tcl_FinalizeNotifier) {
1.192 + tclStubs.tcl_FinalizeNotifier(tsdPtr->clientData);
1.193 + }
1.194 + Tcl_MutexFinalize(&(tsdPtr->queueMutex));
1.195 + for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;
1.196 + prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {
1.197 + if (*prevPtrPtr == tsdPtr) {
1.198 + *prevPtrPtr = tsdPtr->nextPtr;
1.199 + break;
1.200 + }
1.201 + }
1.202 + tsdPtr->initialized = 0;
1.203 +
1.204 + Tcl_MutexUnlock(&listLock);
1.205 +}
1.206 +
1.207 +/*
1.208 + *----------------------------------------------------------------------
1.209 + *
1.210 + * Tcl_SetNotifier --
1.211 + *
1.212 + * Install a set of alternate functions for use with the notifier.
1.213 + # In particular, this can be used to install the Xt-based
1.214 + * notifier for use with the Browser plugin.
1.215 + *
1.216 + * Results:
1.217 + * None.
1.218 + *
1.219 + * Side effects:
1.220 + * Overstomps part of the stub vector. This relies on hooks
1.221 + * added to the default procedures in case those are called
1.222 + * directly (i.e., not through the stub table.)
1.223 + *
1.224 + *----------------------------------------------------------------------
1.225 + */
1.226 +
1.227 +EXPORT_C void
1.228 +Tcl_SetNotifier(notifierProcPtr)
1.229 + Tcl_NotifierProcs *notifierProcPtr;
1.230 +{
1.231 +#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
1.232 + tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc;
1.233 + tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc;
1.234 +#endif
1.235 + tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;
1.236 + tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;
1.237 + tclStubs.tcl_InitNotifier = notifierProcPtr->initNotifierProc;
1.238 + tclStubs.tcl_FinalizeNotifier = notifierProcPtr->finalizeNotifierProc;
1.239 + tclStubs.tcl_AlertNotifier = notifierProcPtr->alertNotifierProc;
1.240 + tclStubs.tcl_ServiceModeHook = notifierProcPtr->serviceModeHookProc;
1.241 +}
1.242 +
1.243 +/*
1.244 + *----------------------------------------------------------------------
1.245 + *
1.246 + * Tcl_CreateEventSource --
1.247 + *
1.248 + * This procedure is invoked to create a new source of events.
1.249 + * The source is identified by a procedure that gets invoked
1.250 + * during Tcl_DoOneEvent to check for events on that source
1.251 + * and queue them.
1.252 + *
1.253 + *
1.254 + * Results:
1.255 + * None.
1.256 + *
1.257 + * Side effects:
1.258 + * SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
1.259 + * runs out of things to do. SetupProc will be invoked before
1.260 + * Tcl_DoOneEvent calls select or whatever else it uses to wait
1.261 + * for events. SetupProc typically calls functions like
1.262 + * Tcl_SetMaxBlockTime to indicate what to wait for.
1.263 + *
1.264 + * CheckProc is called after select or whatever operation was actually
1.265 + * used to wait. It figures out whether anything interesting actually
1.266 + * happened (e.g. by calling Tcl_AsyncReady), and then calls
1.267 + * Tcl_QueueEvent to queue any events that are ready.
1.268 + *
1.269 + * Each of these procedures is passed two arguments, e.g.
1.270 + * (*checkProc)(ClientData clientData, int flags));
1.271 + * ClientData is the same as the clientData argument here, and flags
1.272 + * is a combination of things like TCL_FILE_EVENTS that indicates
1.273 + * what events are of interest: setupProc and checkProc use flags
1.274 + * to figure out whether their events are relevant or not.
1.275 + *
1.276 + *----------------------------------------------------------------------
1.277 + */
1.278 +
1.279 +EXPORT_C void
1.280 +Tcl_CreateEventSource(setupProc, checkProc, clientData)
1.281 + Tcl_EventSetupProc *setupProc; /* Procedure to invoke to figure out
1.282 + * what to wait for. */
1.283 + Tcl_EventCheckProc *checkProc; /* Procedure to call after waiting
1.284 + * to see what happened. */
1.285 + ClientData clientData; /* One-word argument to pass to
1.286 + * setupProc and checkProc. */
1.287 +{
1.288 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.289 + EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
1.290 +
1.291 + sourcePtr->setupProc = setupProc;
1.292 + sourcePtr->checkProc = checkProc;
1.293 + sourcePtr->clientData = clientData;
1.294 + sourcePtr->nextPtr = tsdPtr->firstEventSourcePtr;
1.295 + tsdPtr->firstEventSourcePtr = sourcePtr;
1.296 +}
1.297 +
1.298 +/*
1.299 + *----------------------------------------------------------------------
1.300 + *
1.301 + * Tcl_DeleteEventSource --
1.302 + *
1.303 + * This procedure is invoked to delete the source of events
1.304 + * given by proc and clientData.
1.305 + *
1.306 + * Results:
1.307 + * None.
1.308 + *
1.309 + * Side effects:
1.310 + * The given event source is cancelled, so its procedure will
1.311 + * never again be called. If no such source exists, nothing
1.312 + * happens.
1.313 + *
1.314 + *----------------------------------------------------------------------
1.315 + */
1.316 +
1.317 +EXPORT_C void
1.318 +Tcl_DeleteEventSource(setupProc, checkProc, clientData)
1.319 + Tcl_EventSetupProc *setupProc; /* Procedure to invoke to figure out
1.320 + * what to wait for. */
1.321 + Tcl_EventCheckProc *checkProc; /* Procedure to call after waiting
1.322 + * to see what happened. */
1.323 + ClientData clientData; /* One-word argument to pass to
1.324 + * setupProc and checkProc. */
1.325 +{
1.326 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.327 + EventSource *sourcePtr, *prevPtr;
1.328 +
1.329 + for (sourcePtr = tsdPtr->firstEventSourcePtr, prevPtr = NULL;
1.330 + sourcePtr != NULL;
1.331 + prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
1.332 + if ((sourcePtr->setupProc != setupProc)
1.333 + || (sourcePtr->checkProc != checkProc)
1.334 + || (sourcePtr->clientData != clientData)) {
1.335 + continue;
1.336 + }
1.337 + if (prevPtr == NULL) {
1.338 + tsdPtr->firstEventSourcePtr = sourcePtr->nextPtr;
1.339 + } else {
1.340 + prevPtr->nextPtr = sourcePtr->nextPtr;
1.341 + }
1.342 + ckfree((char *) sourcePtr);
1.343 + return;
1.344 + }
1.345 +}
1.346 +
1.347 +/*
1.348 + *----------------------------------------------------------------------
1.349 + *
1.350 + * Tcl_QueueEvent --
1.351 + *
1.352 + * Queue an event on the event queue associated with the
1.353 + * current thread.
1.354 + *
1.355 + * Results:
1.356 + * None.
1.357 + *
1.358 + * Side effects:
1.359 + * None.
1.360 + *
1.361 + *----------------------------------------------------------------------
1.362 + */
1.363 +
1.364 +EXPORT_C void
1.365 +Tcl_QueueEvent(evPtr, position)
1.366 + Tcl_Event* evPtr; /* Event to add to queue. The storage
1.367 + * space must have been allocated the caller
1.368 + * with malloc (ckalloc), and it becomes
1.369 + * the property of the event queue. It
1.370 + * will be freed after the event has been
1.371 + * handled. */
1.372 + Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
1.373 + * TCL_QUEUE_MARK. */
1.374 +{
1.375 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.376 + QueueEvent(tsdPtr, evPtr, position);
1.377 +}
1.378 +
1.379 +/*
1.380 + *----------------------------------------------------------------------
1.381 + *
1.382 + * Tcl_ThreadQueueEvent --
1.383 + *
1.384 + * Queue an event on the specified thread's event queue.
1.385 + *
1.386 + * Results:
1.387 + * None.
1.388 + *
1.389 + * Side effects:
1.390 + * None.
1.391 + *
1.392 + *----------------------------------------------------------------------
1.393 + */
1.394 +
1.395 +EXPORT_C void
1.396 +Tcl_ThreadQueueEvent(threadId, evPtr, position)
1.397 + Tcl_ThreadId threadId; /* Identifier for thread to use. */
1.398 + Tcl_Event* evPtr; /* Event to add to queue. The storage
1.399 + * space must have been allocated the caller
1.400 + * with malloc (ckalloc), and it becomes
1.401 + * the property of the event queue. It
1.402 + * will be freed after the event has been
1.403 + * handled. */
1.404 + Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
1.405 + * TCL_QUEUE_MARK. */
1.406 +{
1.407 + ThreadSpecificData *tsdPtr;
1.408 +
1.409 + /*
1.410 + * Find the notifier associated with the specified thread.
1.411 + */
1.412 +
1.413 + Tcl_MutexLock(&listLock);
1.414 + for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;
1.415 + tsdPtr = tsdPtr->nextPtr) {
1.416 + /* Empty loop body. */
1.417 + }
1.418 +
1.419 + /*
1.420 + * Queue the event if there was a notifier associated with the thread.
1.421 + */
1.422 +
1.423 + if (tsdPtr) {
1.424 + QueueEvent(tsdPtr, evPtr, position);
1.425 + }
1.426 + Tcl_MutexUnlock(&listLock);
1.427 +}
1.428 +
1.429 +/*
1.430 + *----------------------------------------------------------------------
1.431 + *
1.432 + * QueueEvent --
1.433 + *
1.434 + * Insert an event into the specified thread's event queue at one
1.435 + * of three positions: the head, the tail, or before a floating
1.436 + * marker. Events inserted before the marker will be processed in
1.437 + * first-in-first-out order, but before any events inserted at
1.438 + * the tail of the queue. Events inserted at the head of the
1.439 + * queue will be processed in last-in-first-out order.
1.440 + *
1.441 + * Results:
1.442 + * None.
1.443 + *
1.444 + * Side effects:
1.445 + * None.
1.446 + *
1.447 + *----------------------------------------------------------------------
1.448 + */
1.449 +
1.450 +static void
1.451 +QueueEvent(tsdPtr, evPtr, position)
1.452 + ThreadSpecificData *tsdPtr; /* Handle to thread local data that indicates
1.453 + * which event queue to use. */
1.454 + Tcl_Event* evPtr; /* Event to add to queue. The storage
1.455 + * space must have been allocated the caller
1.456 + * with malloc (ckalloc), and it becomes
1.457 + * the property of the event queue. It
1.458 + * will be freed after the event has been
1.459 + * handled. */
1.460 + Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
1.461 + * TCL_QUEUE_MARK. */
1.462 +{
1.463 + Tcl_MutexLock(&(tsdPtr->queueMutex));
1.464 + if (position == TCL_QUEUE_TAIL) {
1.465 + /*
1.466 + * Append the event on the end of the queue.
1.467 + */
1.468 +
1.469 + evPtr->nextPtr = NULL;
1.470 + if (tsdPtr->firstEventPtr == NULL) {
1.471 + tsdPtr->firstEventPtr = evPtr;
1.472 + } else {
1.473 + tsdPtr->lastEventPtr->nextPtr = evPtr;
1.474 + }
1.475 + tsdPtr->lastEventPtr = evPtr;
1.476 + } else if (position == TCL_QUEUE_HEAD) {
1.477 + /*
1.478 + * Push the event on the head of the queue.
1.479 + */
1.480 +
1.481 + evPtr->nextPtr = tsdPtr->firstEventPtr;
1.482 + if (tsdPtr->firstEventPtr == NULL) {
1.483 + tsdPtr->lastEventPtr = evPtr;
1.484 + }
1.485 + tsdPtr->firstEventPtr = evPtr;
1.486 + } else if (position == TCL_QUEUE_MARK) {
1.487 + /*
1.488 + * Insert the event after the current marker event and advance
1.489 + * the marker to the new event.
1.490 + */
1.491 +
1.492 + if (tsdPtr->markerEventPtr == NULL) {
1.493 + evPtr->nextPtr = tsdPtr->firstEventPtr;
1.494 + tsdPtr->firstEventPtr = evPtr;
1.495 + } else {
1.496 + evPtr->nextPtr = tsdPtr->markerEventPtr->nextPtr;
1.497 + tsdPtr->markerEventPtr->nextPtr = evPtr;
1.498 + }
1.499 + tsdPtr->markerEventPtr = evPtr;
1.500 + if (evPtr->nextPtr == NULL) {
1.501 + tsdPtr->lastEventPtr = evPtr;
1.502 + }
1.503 + }
1.504 + Tcl_MutexUnlock(&(tsdPtr->queueMutex));
1.505 +}
1.506 +
1.507 +/*
1.508 + *----------------------------------------------------------------------
1.509 + *
1.510 + * Tcl_DeleteEvents --
1.511 + *
1.512 + * Calls a procedure for each event in the queue and deletes those
1.513 + * for which the procedure returns 1. Events for which the
1.514 + * procedure returns 0 are left in the queue. Operates on the
1.515 + * queue associated with the current thread.
1.516 + *
1.517 + * Results:
1.518 + * None.
1.519 + *
1.520 + * Side effects:
1.521 + * Potentially removes one or more events from the event queue.
1.522 + *
1.523 + *----------------------------------------------------------------------
1.524 + */
1.525 +
1.526 +EXPORT_C void
1.527 +Tcl_DeleteEvents(proc, clientData)
1.528 + Tcl_EventDeleteProc *proc; /* The procedure to call. */
1.529 + ClientData clientData; /* type-specific data. */
1.530 +{
1.531 + Tcl_Event *evPtr, *prevPtr, *hold;
1.532 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.533 +
1.534 + Tcl_MutexLock(&(tsdPtr->queueMutex));
1.535 + for (prevPtr = (Tcl_Event *) NULL, evPtr = tsdPtr->firstEventPtr;
1.536 + evPtr != (Tcl_Event *) NULL;
1.537 + ) {
1.538 + if ((*proc) (evPtr, clientData) == 1) {
1.539 + if (tsdPtr->firstEventPtr == evPtr) {
1.540 + tsdPtr->firstEventPtr = evPtr->nextPtr;
1.541 + } else {
1.542 + prevPtr->nextPtr = evPtr->nextPtr;
1.543 + }
1.544 + if (evPtr->nextPtr == (Tcl_Event *) NULL) {
1.545 + tsdPtr->lastEventPtr = prevPtr;
1.546 + }
1.547 + if (tsdPtr->markerEventPtr == evPtr) {
1.548 + tsdPtr->markerEventPtr = prevPtr;
1.549 + }
1.550 + hold = evPtr;
1.551 + evPtr = evPtr->nextPtr;
1.552 + ckfree((char *) hold);
1.553 + } else {
1.554 + prevPtr = evPtr;
1.555 + evPtr = evPtr->nextPtr;
1.556 + }
1.557 + }
1.558 + Tcl_MutexUnlock(&(tsdPtr->queueMutex));
1.559 +}
1.560 +
1.561 +/*
1.562 + *----------------------------------------------------------------------
1.563 + *
1.564 + * Tcl_ServiceEvent --
1.565 + *
1.566 + * Process one event from the event queue, or invoke an
1.567 + * asynchronous event handler. Operates on event queue for
1.568 + * current thread.
1.569 + *
1.570 + * Results:
1.571 + * The return value is 1 if the procedure actually found an event
1.572 + * to process. If no processing occurred, then 0 is returned.
1.573 + *
1.574 + * Side effects:
1.575 + * Invokes all of the event handlers for the highest priority
1.576 + * event in the event queue. May collapse some events into a
1.577 + * single event or discard stale events.
1.578 + *
1.579 + *----------------------------------------------------------------------
1.580 + */
1.581 +
1.582 +EXPORT_C int
1.583 +Tcl_ServiceEvent(flags)
1.584 + int flags; /* Indicates what events should be processed.
1.585 + * May be any combination of TCL_WINDOW_EVENTS
1.586 + * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
1.587 + * flags defined elsewhere. Events not
1.588 + * matching this will be skipped for processing
1.589 + * later. */
1.590 +{
1.591 + Tcl_Event *evPtr, *prevPtr;
1.592 + Tcl_EventProc *proc;
1.593 + int result;
1.594 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.595 +
1.596 + /*
1.597 + * Asynchronous event handlers are considered to be the highest
1.598 + * priority events, and so must be invoked before we process events
1.599 + * on the event queue.
1.600 + */
1.601 +
1.602 + if (Tcl_AsyncReady()) {
1.603 + (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
1.604 + return 1;
1.605 + }
1.606 +
1.607 + /*
1.608 + * No event flags is equivalent to TCL_ALL_EVENTS.
1.609 + */
1.610 +
1.611 + if ((flags & TCL_ALL_EVENTS) == 0) {
1.612 + flags |= TCL_ALL_EVENTS;
1.613 + }
1.614 +
1.615 + /*
1.616 + * Loop through all the events in the queue until we find one
1.617 + * that can actually be handled.
1.618 + */
1.619 +
1.620 + Tcl_MutexLock(&(tsdPtr->queueMutex));
1.621 + for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL;
1.622 + evPtr = evPtr->nextPtr) {
1.623 + /*
1.624 + * Call the handler for the event. If it actually handles the
1.625 + * event then free the storage for the event. There are two
1.626 + * tricky things here, both stemming from the fact that the event
1.627 + * code may be re-entered while servicing the event:
1.628 + *
1.629 + * 1. Set the "proc" field to NULL. This is a signal to ourselves
1.630 + * that we shouldn't reexecute the handler if the event loop
1.631 + * is re-entered.
1.632 + * 2. When freeing the event, must search the queue again from the
1.633 + * front to find it. This is because the event queue could
1.634 + * change almost arbitrarily while handling the event, so we
1.635 + * can't depend on pointers found now still being valid when
1.636 + * the handler returns.
1.637 + */
1.638 +
1.639 + proc = evPtr->proc;
1.640 + if (proc == NULL) {
1.641 + continue;
1.642 + }
1.643 + evPtr->proc = NULL;
1.644 +
1.645 + /*
1.646 + * Release the lock before calling the event procedure. This
1.647 + * allows other threads to post events if we enter a recursive
1.648 + * event loop in this thread. Note that we are making the assumption
1.649 + * that if the proc returns 0, the event is still in the list.
1.650 + */
1.651 +
1.652 + Tcl_MutexUnlock(&(tsdPtr->queueMutex));
1.653 + result = (*proc)(evPtr, flags);
1.654 + Tcl_MutexLock(&(tsdPtr->queueMutex));
1.655 +
1.656 + if (result) {
1.657 + /*
1.658 + * The event was processed, so remove it from the queue.
1.659 + */
1.660 +
1.661 + if (tsdPtr->firstEventPtr == evPtr) {
1.662 + tsdPtr->firstEventPtr = evPtr->nextPtr;
1.663 + if (evPtr->nextPtr == NULL) {
1.664 + tsdPtr->lastEventPtr = NULL;
1.665 + }
1.666 + if (tsdPtr->markerEventPtr == evPtr) {
1.667 + tsdPtr->markerEventPtr = NULL;
1.668 + }
1.669 + } else {
1.670 + for (prevPtr = tsdPtr->firstEventPtr;
1.671 + prevPtr && prevPtr->nextPtr != evPtr;
1.672 + prevPtr = prevPtr->nextPtr) {
1.673 + /* Empty loop body. */
1.674 + }
1.675 + if (prevPtr) {
1.676 + prevPtr->nextPtr = evPtr->nextPtr;
1.677 + if (evPtr->nextPtr == NULL) {
1.678 + tsdPtr->lastEventPtr = prevPtr;
1.679 + }
1.680 + if (tsdPtr->markerEventPtr == evPtr) {
1.681 + tsdPtr->markerEventPtr = prevPtr;
1.682 + }
1.683 + } else {
1.684 + evPtr = NULL;
1.685 + }
1.686 + }
1.687 + if (evPtr) {
1.688 + ckfree((char *) evPtr);
1.689 + }
1.690 + Tcl_MutexUnlock(&(tsdPtr->queueMutex));
1.691 + return 1;
1.692 + } else {
1.693 + /*
1.694 + * The event wasn't actually handled, so we have to restore
1.695 + * the proc field to allow the event to be attempted again.
1.696 + */
1.697 +
1.698 + evPtr->proc = proc;
1.699 + }
1.700 + }
1.701 + Tcl_MutexUnlock(&(tsdPtr->queueMutex));
1.702 + return 0;
1.703 +}
1.704 +
1.705 +/*
1.706 + *----------------------------------------------------------------------
1.707 + *
1.708 + * Tcl_GetServiceMode --
1.709 + *
1.710 + * This routine returns the current service mode of the notifier.
1.711 + *
1.712 + * Results:
1.713 + * Returns either TCL_SERVICE_ALL or TCL_SERVICE_NONE.
1.714 + *
1.715 + * Side effects:
1.716 + * None.
1.717 + *
1.718 + *----------------------------------------------------------------------
1.719 + */
1.720 +
1.721 +EXPORT_C int
1.722 +Tcl_GetServiceMode()
1.723 +{
1.724 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.725 +
1.726 + return tsdPtr->serviceMode;
1.727 +}
1.728 +
1.729 +/*
1.730 + *----------------------------------------------------------------------
1.731 + *
1.732 + * Tcl_SetServiceMode --
1.733 + *
1.734 + * This routine sets the current service mode of the tsdPtr->
1.735 + *
1.736 + * Results:
1.737 + * Returns the previous service mode.
1.738 + *
1.739 + * Side effects:
1.740 + * Invokes the notifier service mode hook procedure.
1.741 + *
1.742 + *----------------------------------------------------------------------
1.743 + */
1.744 +
1.745 +EXPORT_C int
1.746 +Tcl_SetServiceMode(mode)
1.747 + int mode; /* New service mode: TCL_SERVICE_ALL or
1.748 + * TCL_SERVICE_NONE */
1.749 +{
1.750 + int oldMode;
1.751 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.752 +
1.753 + oldMode = tsdPtr->serviceMode;
1.754 + tsdPtr->serviceMode = mode;
1.755 + if (tclStubs.tcl_ServiceModeHook) {
1.756 + tclStubs.tcl_ServiceModeHook(mode);
1.757 + }
1.758 + return oldMode;
1.759 +}
1.760 +
1.761 +/*
1.762 + *----------------------------------------------------------------------
1.763 + *
1.764 + * Tcl_SetMaxBlockTime --
1.765 + *
1.766 + * This procedure is invoked by event sources to tell the notifier
1.767 + * how long it may block the next time it blocks. The timePtr
1.768 + * argument gives a maximum time; the actual time may be less if
1.769 + * some other event source requested a smaller time.
1.770 + *
1.771 + * Results:
1.772 + * None.
1.773 + *
1.774 + * Side effects:
1.775 + * May reduce the length of the next sleep in the tsdPtr->
1.776 + *
1.777 + *----------------------------------------------------------------------
1.778 + */
1.779 +
1.780 +EXPORT_C void
1.781 +Tcl_SetMaxBlockTime(timePtr)
1.782 + Tcl_Time *timePtr; /* Specifies a maximum elapsed time for
1.783 + * the next blocking operation in the
1.784 + * event tsdPtr-> */
1.785 +{
1.786 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.787 +
1.788 + if (!tsdPtr->blockTimeSet || (timePtr->sec < tsdPtr->blockTime.sec)
1.789 + || ((timePtr->sec == tsdPtr->blockTime.sec)
1.790 + && (timePtr->usec < tsdPtr->blockTime.usec))) {
1.791 + tsdPtr->blockTime = *timePtr;
1.792 + tsdPtr->blockTimeSet = 1;
1.793 + }
1.794 +
1.795 + /*
1.796 + * If we are called outside an event source traversal, set the
1.797 + * timeout immediately.
1.798 + */
1.799 +
1.800 + if (!tsdPtr->inTraversal) {
1.801 + if (tsdPtr->blockTimeSet) {
1.802 + Tcl_SetTimer(&tsdPtr->blockTime);
1.803 + } else {
1.804 + Tcl_SetTimer(NULL);
1.805 + }
1.806 + }
1.807 +}
1.808 +
1.809 +/*
1.810 + *----------------------------------------------------------------------
1.811 + *
1.812 + * Tcl_DoOneEvent --
1.813 + *
1.814 + * Process a single event of some sort. If there's no work to
1.815 + * do, wait for an event to occur, then process it.
1.816 + *
1.817 + * Results:
1.818 + * The return value is 1 if the procedure actually found an event
1.819 + * to process. If no processing occurred, then 0 is returned (this
1.820 + * can happen if the TCL_DONT_WAIT flag is set or if there are no
1.821 + * event handlers to wait for in the set specified by flags).
1.822 + *
1.823 + * Side effects:
1.824 + * May delay execution of process while waiting for an event,
1.825 + * unless TCL_DONT_WAIT is set in the flags argument. Event
1.826 + * sources are invoked to check for and queue events. Event
1.827 + * handlers may produce arbitrary side effects.
1.828 + *
1.829 + *----------------------------------------------------------------------
1.830 + */
1.831 +
1.832 +EXPORT_C int
1.833 +Tcl_DoOneEvent(flags)
1.834 + int flags; /* Miscellaneous flag values: may be any
1.835 + * combination of TCL_DONT_WAIT,
1.836 + * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
1.837 + * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
1.838 + * others defined by event sources. */
1.839 +{
1.840 + int result = 0, oldMode;
1.841 + EventSource *sourcePtr;
1.842 + Tcl_Time *timePtr;
1.843 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.844 +
1.845 + /*
1.846 + * The first thing we do is to service any asynchronous event
1.847 + * handlers.
1.848 + */
1.849 +
1.850 + if (Tcl_AsyncReady()) {
1.851 + (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
1.852 + return 1;
1.853 + }
1.854 +
1.855 + /*
1.856 + * No event flags is equivalent to TCL_ALL_EVENTS.
1.857 + */
1.858 +
1.859 + if ((flags & TCL_ALL_EVENTS) == 0) {
1.860 + flags |= TCL_ALL_EVENTS;
1.861 + }
1.862 +
1.863 + /*
1.864 + * Set the service mode to none so notifier event routines won't
1.865 + * try to service events recursively.
1.866 + */
1.867 +
1.868 + oldMode = tsdPtr->serviceMode;
1.869 + tsdPtr->serviceMode = TCL_SERVICE_NONE;
1.870 +
1.871 + /*
1.872 + * The core of this procedure is an infinite loop, even though
1.873 + * we only service one event. The reason for this is that we
1.874 + * may be processing events that don't do anything inside of Tcl.
1.875 + */
1.876 +
1.877 + while (1) {
1.878 +
1.879 + /*
1.880 + * If idle events are the only things to service, skip the
1.881 + * main part of the loop and go directly to handle idle
1.882 + * events (i.e. don't wait even if TCL_DONT_WAIT isn't set).
1.883 + */
1.884 +
1.885 + if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {
1.886 + flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
1.887 + goto idleEvents;
1.888 + }
1.889 +
1.890 + /*
1.891 + * Ask Tcl to service a queued event, if there are any.
1.892 + */
1.893 +
1.894 + if (Tcl_ServiceEvent(flags)) {
1.895 + result = 1;
1.896 + break;
1.897 + }
1.898 +
1.899 + /*
1.900 + * If TCL_DONT_WAIT is set, be sure to poll rather than
1.901 + * blocking, otherwise reset the block time to infinity.
1.902 + */
1.903 +
1.904 + if (flags & TCL_DONT_WAIT) {
1.905 + tsdPtr->blockTime.sec = 0;
1.906 + tsdPtr->blockTime.usec = 0;
1.907 + tsdPtr->blockTimeSet = 1;
1.908 + } else {
1.909 + tsdPtr->blockTimeSet = 0;
1.910 + }
1.911 +
1.912 + /*
1.913 + * Set up all the event sources for new events. This will
1.914 + * cause the block time to be updated if necessary.
1.915 + */
1.916 +
1.917 + tsdPtr->inTraversal = 1;
1.918 + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1.919 + sourcePtr = sourcePtr->nextPtr) {
1.920 + if (sourcePtr->setupProc) {
1.921 + (sourcePtr->setupProc)(sourcePtr->clientData, flags);
1.922 + }
1.923 + }
1.924 + tsdPtr->inTraversal = 0;
1.925 +
1.926 + if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) {
1.927 + timePtr = &tsdPtr->blockTime;
1.928 + } else {
1.929 + timePtr = NULL;
1.930 + }
1.931 +
1.932 + /*
1.933 + * Wait for a new event or a timeout. If Tcl_WaitForEvent
1.934 + * returns -1, we should abort Tcl_DoOneEvent.
1.935 + */
1.936 +
1.937 + result = Tcl_WaitForEvent(timePtr);
1.938 + if (result < 0) {
1.939 + result = 0;
1.940 + break;
1.941 + }
1.942 +
1.943 + /*
1.944 + * Check all the event sources for new events.
1.945 + */
1.946 +
1.947 + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1.948 + sourcePtr = sourcePtr->nextPtr) {
1.949 + if (sourcePtr->checkProc) {
1.950 + (sourcePtr->checkProc)(sourcePtr->clientData, flags);
1.951 + }
1.952 + }
1.953 +
1.954 + /*
1.955 + * Check for events queued by the notifier or event sources.
1.956 + */
1.957 +
1.958 + if (Tcl_ServiceEvent(flags)) {
1.959 + result = 1;
1.960 + break;
1.961 + }
1.962 +
1.963 + /*
1.964 + * We've tried everything at this point, but nobody we know
1.965 + * about had anything to do. Check for idle events. If none,
1.966 + * either quit or go back to the top and try again.
1.967 + */
1.968 +
1.969 + idleEvents:
1.970 + if (flags & TCL_IDLE_EVENTS) {
1.971 + if (TclServiceIdle()) {
1.972 + result = 1;
1.973 + break;
1.974 + }
1.975 + }
1.976 + if (flags & TCL_DONT_WAIT) {
1.977 + break;
1.978 + }
1.979 +
1.980 + /*
1.981 + * If Tcl_WaitForEvent has returned 1,
1.982 + * indicating that one system event has been dispatched
1.983 + * (and thus that some Tcl code might have been indirectly executed),
1.984 + * we break out of the loop.
1.985 + * We do this to give VwaitCmd for instance a chance to check
1.986 + * if that system event had the side effect of changing the
1.987 + * variable (so the vwait can return and unwind properly).
1.988 + *
1.989 + * NB: We will process idle events if any first, because
1.990 + * otherwise we might never do the idle events if the notifier
1.991 + * always gets system events.
1.992 + */
1.993 +
1.994 + if (result) {
1.995 + break;
1.996 + }
1.997 +
1.998 + }
1.999 +
1.1000 + tsdPtr->serviceMode = oldMode;
1.1001 + return result;
1.1002 +}
1.1003 +
1.1004 +/*
1.1005 + *----------------------------------------------------------------------
1.1006 + *
1.1007 + * Tcl_ServiceAll --
1.1008 + *
1.1009 + * This routine checks all of the event sources, processes
1.1010 + * events that are on the Tcl event queue, and then calls the
1.1011 + * any idle handlers. Platform specific notifier callbacks that
1.1012 + * generate events should call this routine before returning to
1.1013 + * the system in order to ensure that Tcl gets a chance to
1.1014 + * process the new events.
1.1015 + *
1.1016 + * Results:
1.1017 + * Returns 1 if an event or idle handler was invoked, else 0.
1.1018 + *
1.1019 + * Side effects:
1.1020 + * Anything that an event or idle handler may do.
1.1021 + *
1.1022 + *----------------------------------------------------------------------
1.1023 + */
1.1024 +
1.1025 +EXPORT_C int
1.1026 +Tcl_ServiceAll()
1.1027 +{
1.1028 + int result = 0;
1.1029 + EventSource *sourcePtr;
1.1030 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1031 +
1.1032 + if (tsdPtr->serviceMode == TCL_SERVICE_NONE) {
1.1033 + return result;
1.1034 + }
1.1035 +
1.1036 + /*
1.1037 + * We need to turn off event servicing like we to in Tcl_DoOneEvent,
1.1038 + * to avoid recursive calls.
1.1039 + */
1.1040 +
1.1041 + tsdPtr->serviceMode = TCL_SERVICE_NONE;
1.1042 +
1.1043 + /*
1.1044 + * Check async handlers first.
1.1045 + */
1.1046 +
1.1047 + if (Tcl_AsyncReady()) {
1.1048 + (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
1.1049 + }
1.1050 +
1.1051 + /*
1.1052 + * Make a single pass through all event sources, queued events,
1.1053 + * and idle handlers. Note that we wait to update the notifier
1.1054 + * timer until the end so we can avoid multiple changes.
1.1055 + */
1.1056 +
1.1057 + tsdPtr->inTraversal = 1;
1.1058 + tsdPtr->blockTimeSet = 0;
1.1059 +
1.1060 + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1.1061 + sourcePtr = sourcePtr->nextPtr) {
1.1062 + if (sourcePtr->setupProc) {
1.1063 + (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
1.1064 + }
1.1065 + }
1.1066 + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1.1067 + sourcePtr = sourcePtr->nextPtr) {
1.1068 + if (sourcePtr->checkProc) {
1.1069 + (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
1.1070 + }
1.1071 + }
1.1072 +
1.1073 + while (Tcl_ServiceEvent(0)) {
1.1074 + result = 1;
1.1075 + }
1.1076 + if (TclServiceIdle()) {
1.1077 + result = 1;
1.1078 + }
1.1079 +
1.1080 + if (!tsdPtr->blockTimeSet) {
1.1081 + Tcl_SetTimer(NULL);
1.1082 + } else {
1.1083 + Tcl_SetTimer(&tsdPtr->blockTime);
1.1084 + }
1.1085 + tsdPtr->inTraversal = 0;
1.1086 + tsdPtr->serviceMode = TCL_SERVICE_ALL;
1.1087 + return result;
1.1088 +}
1.1089 +
1.1090 +/*
1.1091 + *----------------------------------------------------------------------
1.1092 + *
1.1093 + * Tcl_ThreadAlert --
1.1094 + *
1.1095 + * This function wakes up the notifier associated with the
1.1096 + * specified thread (if there is one).
1.1097 + *
1.1098 + * Results:
1.1099 + * None.
1.1100 + *
1.1101 + * Side effects:
1.1102 + * None.
1.1103 + *
1.1104 + *----------------------------------------------------------------------
1.1105 + */
1.1106 +
1.1107 +EXPORT_C void
1.1108 +Tcl_ThreadAlert(threadId)
1.1109 + Tcl_ThreadId threadId; /* Identifier for thread to use. */
1.1110 +{
1.1111 + ThreadSpecificData *tsdPtr;
1.1112 +
1.1113 + /*
1.1114 + * Find the notifier associated with the specified thread.
1.1115 + * Note that we need to hold the listLock while calling
1.1116 + * Tcl_AlertNotifier to avoid a race condition where
1.1117 + * the specified thread might destroy its notifier.
1.1118 + */
1.1119 +
1.1120 + Tcl_MutexLock(&listLock);
1.1121 + for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1.1122 + if (tsdPtr->threadId == threadId) {
1.1123 + if (tclStubs.tcl_AlertNotifier) {
1.1124 + tclStubs.tcl_AlertNotifier(tsdPtr->clientData);
1.1125 + }
1.1126 + break;
1.1127 + }
1.1128 + }
1.1129 + Tcl_MutexUnlock(&listLock);
1.1130 +}