tclNotify.cGo to the documentation of this file.00001 /* 00002 * tclNotify.c -- 00003 * 00004 * This file implements the generic portion of the Tcl notifier. The 00005 * notifier is lowest-level part of the event system. It manages an event 00006 * queue that holds Tcl_Event structures. The platform specific portion 00007 * of the notifier is defined in the tcl*Notify.c files in each platform 00008 * directory. 00009 * 00010 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 00011 * Copyright (c) 1998 by Scriptics Corporation. 00012 * Copyright (c) 2003 by Kevin B. Kenny. All rights reserved. 00013 * 00014 * See the file "license.terms" for information on usage and redistribution of 00015 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00016 * 00017 * RCS: @(#) $Id: tclNotify.c,v 1.25 2006/09/25 15:02:54 dkf Exp $ 00018 */ 00019 00020 #include "tclInt.h" 00021 00022 extern TclStubs tclStubs; 00023 00024 /* 00025 * For each event source (created with Tcl_CreateEventSource) there is a 00026 * structure of the following type: 00027 */ 00028 00029 typedef struct EventSource { 00030 Tcl_EventSetupProc *setupProc; 00031 Tcl_EventCheckProc *checkProc; 00032 ClientData clientData; 00033 struct EventSource *nextPtr; 00034 } EventSource; 00035 00036 /* 00037 * The following structure keeps track of the state of the notifier on a 00038 * per-thread basis. The first three elements keep track of the event queue. 00039 * In addition to the first (next to be serviced) and last events in the 00040 * queue, we keep track of a "marker" event. This provides a simple priority 00041 * mechanism whereby events can be inserted at the front of the queue but 00042 * behind all other high-priority events already in the queue (this is used 00043 * for things like a sequence of Enter and Leave events generated during a 00044 * grab in Tk). These elements are protected by the queueMutex so that any 00045 * thread can queue an event on any notifier. Note that all of the values in 00046 * this structure will be initialized to 0. 00047 */ 00048 00049 typedef struct ThreadSpecificData { 00050 Tcl_Event *firstEventPtr; /* First pending event, or NULL if none. */ 00051 Tcl_Event *lastEventPtr; /* Last pending event, or NULL if none. */ 00052 Tcl_Event *markerEventPtr; /* Last high-priority event in queue, or NULL 00053 * if none. */ 00054 Tcl_Mutex queueMutex; /* Mutex to protect access to the previous 00055 * three fields. */ 00056 int serviceMode; /* One of TCL_SERVICE_NONE or 00057 * TCL_SERVICE_ALL. */ 00058 int blockTimeSet; /* 0 means there is no maximum block time: 00059 * block forever. */ 00060 Tcl_Time blockTime; /* If blockTimeSet is 1, gives the maximum 00061 * elapsed time for the next block. */ 00062 int inTraversal; /* 1 if Tcl_SetMaxBlockTime is being called 00063 * during an event source traversal. */ 00064 EventSource *firstEventSourcePtr; 00065 /* Pointer to first event source in list of 00066 * event sources for this thread. */ 00067 Tcl_ThreadId threadId; /* Thread that owns this notifier instance. */ 00068 ClientData clientData; /* Opaque handle for platform specific 00069 * notifier. */ 00070 int initialized; /* 1 if notifier has been initialized. */ 00071 struct ThreadSpecificData *nextPtr; 00072 /* Next notifier in global list of notifiers. 00073 * Access is controlled by the listLock global 00074 * mutex. */ 00075 } ThreadSpecificData; 00076 00077 static Tcl_ThreadDataKey dataKey; 00078 00079 /* 00080 * Global list of notifiers. Access to this list is controlled by the listLock 00081 * mutex. If this becomes a performance bottleneck, this could be replaced 00082 * with a hashtable. 00083 */ 00084 00085 static ThreadSpecificData *firstNotifierPtr = NULL; 00086 TCL_DECLARE_MUTEX(listLock) 00087 00088 /* 00089 * Declarations for routines used only in this file. 00090 */ 00091 00092 static void QueueEvent(ThreadSpecificData *tsdPtr, 00093 Tcl_Event* evPtr, Tcl_QueuePosition position); 00094 00095 /* 00096 *---------------------------------------------------------------------- 00097 * 00098 * TclInitNotifier -- 00099 * 00100 * Initialize the thread local data structures for the notifier 00101 * subsystem. 00102 * 00103 * Results: 00104 * None. 00105 * 00106 * Side effects: 00107 * Adds the current thread to the global list of notifiers. 00108 * 00109 *---------------------------------------------------------------------- 00110 */ 00111 00112 void 00113 TclInitNotifier(void) 00114 { 00115 ThreadSpecificData *tsdPtr; 00116 Tcl_ThreadId threadId = Tcl_GetCurrentThread(); 00117 00118 Tcl_MutexLock(&listLock); 00119 for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId; 00120 tsdPtr = tsdPtr->nextPtr) { 00121 /* Empty loop body. */ 00122 } 00123 00124 if (NULL == tsdPtr) { 00125 /* 00126 * Notifier not yet initialized in this thread. 00127 */ 00128 00129 tsdPtr = TCL_TSD_INIT(&dataKey); 00130 tsdPtr->threadId = threadId; 00131 tsdPtr->clientData = tclStubs.tcl_InitNotifier(); 00132 tsdPtr->initialized = 1; 00133 tsdPtr->nextPtr = firstNotifierPtr; 00134 firstNotifierPtr = tsdPtr; 00135 } 00136 Tcl_MutexUnlock(&listLock); 00137 } 00138 00139 /* 00140 *---------------------------------------------------------------------- 00141 * 00142 * TclFinalizeNotifier -- 00143 * 00144 * Finalize the thread local data structures for the notifier subsystem. 00145 * 00146 * Results: 00147 * None. 00148 * 00149 * Side effects: 00150 * Removes the notifier associated with the current thread from the 00151 * global notifier list. This is done only if the notifier was 00152 * initialized for this thread by call to TclInitNotifier(). This is 00153 * always true for threads which have been seeded with an Tcl 00154 * interpreter, since the call to Tcl_CreateInterp will, among other 00155 * things, call TclInitializeSubsystems() and this one will, in turn, 00156 * call the TclInitNotifier() for the thread. For threads created without 00157 * the Tcl interpreter, though, nobody is explicitly nor implicitly 00158 * calling the TclInitNotifier hence, TclFinalizeNotifier should not be 00159 * performed at all. 00160 * 00161 *---------------------------------------------------------------------- 00162 */ 00163 00164 void 00165 TclFinalizeNotifier(void) 00166 { 00167 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00168 ThreadSpecificData **prevPtrPtr; 00169 Tcl_Event *evPtr, *hold; 00170 00171 if (!tsdPtr->initialized) { 00172 return; /* Notifier not initialized for the current thread */ 00173 } 00174 00175 Tcl_MutexLock(&(tsdPtr->queueMutex)); 00176 for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL; ) { 00177 hold = evPtr; 00178 evPtr = evPtr->nextPtr; 00179 ckfree((char *) hold); 00180 } 00181 tsdPtr->firstEventPtr = NULL; 00182 tsdPtr->lastEventPtr = NULL; 00183 Tcl_MutexUnlock(&(tsdPtr->queueMutex)); 00184 00185 Tcl_MutexLock(&listLock); 00186 00187 if (tclStubs.tcl_FinalizeNotifier) { 00188 tclStubs.tcl_FinalizeNotifier(tsdPtr->clientData); 00189 } 00190 Tcl_MutexFinalize(&(tsdPtr->queueMutex)); 00191 for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL; 00192 prevPtrPtr = &((*prevPtrPtr)->nextPtr)) { 00193 if (*prevPtrPtr == tsdPtr) { 00194 *prevPtrPtr = tsdPtr->nextPtr; 00195 break; 00196 } 00197 } 00198 tsdPtr->initialized = 0; 00199 00200 Tcl_MutexUnlock(&listLock); 00201 } 00202 00203 /* 00204 *---------------------------------------------------------------------- 00205 * 00206 * Tcl_SetNotifier -- 00207 * 00208 * Install a set of alternate functions for use with the notifier. In 00209 * particular, this can be used to install the Xt-based notifier for use 00210 * with the Browser plugin. 00211 * 00212 * Results: 00213 * None. 00214 * 00215 * Side effects: 00216 * Overstomps part of the stub vector. This relies on hooks added to the 00217 * default functions in case those are called directly (i.e., not through 00218 * the stub table.) 00219 * 00220 *---------------------------------------------------------------------- 00221 */ 00222 00223 void 00224 Tcl_SetNotifier( 00225 Tcl_NotifierProcs *notifierProcPtr) 00226 { 00227 #if !defined(__WIN32__) /* UNIX */ 00228 tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc; 00229 tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc; 00230 #endif 00231 tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc; 00232 tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc; 00233 tclStubs.tcl_InitNotifier = notifierProcPtr->initNotifierProc; 00234 tclStubs.tcl_FinalizeNotifier = notifierProcPtr->finalizeNotifierProc; 00235 tclStubs.tcl_AlertNotifier = notifierProcPtr->alertNotifierProc; 00236 tclStubs.tcl_ServiceModeHook = notifierProcPtr->serviceModeHookProc; 00237 } 00238 00239 /* 00240 *---------------------------------------------------------------------- 00241 * 00242 * Tcl_CreateEventSource -- 00243 * 00244 * This function is invoked to create a new source of events. The source 00245 * is identified by a function that gets invoked during Tcl_DoOneEvent to 00246 * check for events on that source and queue them. 00247 * 00248 * 00249 * Results: 00250 * None. 00251 * 00252 * Side effects: 00253 * SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent 00254 * runs out of things to do. SetupProc will be invoked before 00255 * Tcl_DoOneEvent calls select or whatever else it uses to wait for 00256 * events. SetupProc typically calls functions like Tcl_SetMaxBlockTime 00257 * to indicate what to wait for. 00258 * 00259 * CheckProc is called after select or whatever operation was actually 00260 * used to wait. It figures out whether anything interesting actually 00261 * happened (e.g. by calling Tcl_AsyncReady), and then calls 00262 * Tcl_QueueEvent to queue any events that are ready. 00263 * 00264 * Each of these functions is passed two arguments, e.g. 00265 * (*checkProc)(ClientData clientData, int flags)); 00266 * ClientData is the same as the clientData argument here, and flags is a 00267 * combination of things like TCL_FILE_EVENTS that indicates what events 00268 * are of interest: setupProc and checkProc use flags to figure out 00269 * whether their events are relevant or not. 00270 * 00271 *---------------------------------------------------------------------- 00272 */ 00273 00274 void 00275 Tcl_CreateEventSource( 00276 Tcl_EventSetupProc *setupProc, 00277 /* Function to invoke to figure out what to 00278 * wait for. */ 00279 Tcl_EventCheckProc *checkProc, 00280 /* Function to call after waiting to see what 00281 * happened. */ 00282 ClientData clientData) /* One-word argument to pass to setupProc and 00283 * checkProc. */ 00284 { 00285 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00286 EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource)); 00287 00288 sourcePtr->setupProc = setupProc; 00289 sourcePtr->checkProc = checkProc; 00290 sourcePtr->clientData = clientData; 00291 sourcePtr->nextPtr = tsdPtr->firstEventSourcePtr; 00292 tsdPtr->firstEventSourcePtr = sourcePtr; 00293 } 00294 00295 /* 00296 *---------------------------------------------------------------------- 00297 * 00298 * Tcl_DeleteEventSource -- 00299 * 00300 * This function is invoked to delete the source of events given by proc 00301 * and clientData. 00302 * 00303 * Results: 00304 * None. 00305 * 00306 * Side effects: 00307 * The given event source is cancelled, so its function will never again 00308 * be called. If no such source exists, nothing happens. 00309 * 00310 *---------------------------------------------------------------------- 00311 */ 00312 00313 void 00314 Tcl_DeleteEventSource( 00315 Tcl_EventSetupProc *setupProc, 00316 /* Function to invoke to figure out what to 00317 * wait for. */ 00318 Tcl_EventCheckProc *checkProc, 00319 /* Function to call after waiting to see what 00320 * happened. */ 00321 ClientData clientData) /* One-word argument to pass to setupProc and 00322 * checkProc. */ 00323 { 00324 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00325 EventSource *sourcePtr, *prevPtr; 00326 00327 for (sourcePtr = tsdPtr->firstEventSourcePtr, prevPtr = NULL; 00328 sourcePtr != NULL; 00329 prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) { 00330 if ((sourcePtr->setupProc != setupProc) 00331 || (sourcePtr->checkProc != checkProc) 00332 || (sourcePtr->clientData != clientData)) { 00333 continue; 00334 } 00335 if (prevPtr == NULL) { 00336 tsdPtr->firstEventSourcePtr = sourcePtr->nextPtr; 00337 } else { 00338 prevPtr->nextPtr = sourcePtr->nextPtr; 00339 } 00340 ckfree((char *) sourcePtr); 00341 return; 00342 } 00343 } 00344 00345 /* 00346 *---------------------------------------------------------------------- 00347 * 00348 * Tcl_QueueEvent -- 00349 * 00350 * Queue an event on the event queue associated with the current thread. 00351 * 00352 * Results: 00353 * None. 00354 * 00355 * Side effects: 00356 * None. 00357 * 00358 *---------------------------------------------------------------------- 00359 */ 00360 00361 void 00362 Tcl_QueueEvent( 00363 Tcl_Event* evPtr, /* Event to add to queue. The storage space 00364 * must have been allocated the caller with 00365 * malloc (ckalloc), and it becomes the 00366 * property of the event queue. It will be 00367 * freed after the event has been handled. */ 00368 Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, 00369 * TCL_QUEUE_MARK. */ 00370 { 00371 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00372 QueueEvent(tsdPtr, evPtr, position); 00373 } 00374 00375 /* 00376 *---------------------------------------------------------------------- 00377 * 00378 * Tcl_ThreadQueueEvent -- 00379 * 00380 * Queue an event on the specified thread's event queue. 00381 * 00382 * Results: 00383 * None. 00384 * 00385 * Side effects: 00386 * None. 00387 * 00388 *---------------------------------------------------------------------- 00389 */ 00390 00391 void 00392 Tcl_ThreadQueueEvent( 00393 Tcl_ThreadId threadId, /* Identifier for thread to use. */ 00394 Tcl_Event *evPtr, /* Event to add to queue. The storage space 00395 * must have been allocated the caller with 00396 * malloc (ckalloc), and it becomes the 00397 * property of the event queue. It will be 00398 * freed after the event has been handled. */ 00399 Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, 00400 * TCL_QUEUE_MARK. */ 00401 { 00402 ThreadSpecificData *tsdPtr; 00403 00404 /* 00405 * Find the notifier associated with the specified thread. 00406 */ 00407 00408 Tcl_MutexLock(&listLock); 00409 for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId; 00410 tsdPtr = tsdPtr->nextPtr) { 00411 /* Empty loop body. */ 00412 } 00413 00414 /* 00415 * Queue the event if there was a notifier associated with the thread. 00416 */ 00417 00418 if (tsdPtr) { 00419 QueueEvent(tsdPtr, evPtr, position); 00420 } 00421 Tcl_MutexUnlock(&listLock); 00422 } 00423 00424 /* 00425 *---------------------------------------------------------------------- 00426 * 00427 * QueueEvent -- 00428 * 00429 * Insert an event into the specified thread's event queue at one of 00430 * three positions: the head, the tail, or before a floating marker. 00431 * Events inserted before the marker will be processed in first-in- 00432 * first-out order, but before any events inserted at the tail of the 00433 * queue. Events inserted at the head of the queue will be processed in 00434 * last-in-first-out order. 00435 * 00436 * Results: 00437 * None. 00438 * 00439 * Side effects: 00440 * None. 00441 * 00442 *---------------------------------------------------------------------- 00443 */ 00444 00445 static void 00446 QueueEvent( 00447 ThreadSpecificData *tsdPtr, /* Handle to thread local data that indicates 00448 * which event queue to use. */ 00449 Tcl_Event *evPtr, /* Event to add to queue. The storage space 00450 * must have been allocated the caller with 00451 * malloc (ckalloc), and it becomes the 00452 * property of the event queue. It will be 00453 * freed after the event has been handled. */ 00454 Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, 00455 * TCL_QUEUE_MARK. */ 00456 { 00457 Tcl_MutexLock(&(tsdPtr->queueMutex)); 00458 if (position == TCL_QUEUE_TAIL) { 00459 /* 00460 * Append the event on the end of the queue. 00461 */ 00462 00463 evPtr->nextPtr = NULL; 00464 if (tsdPtr->firstEventPtr == NULL) { 00465 tsdPtr->firstEventPtr = evPtr; 00466 } else { 00467 tsdPtr->lastEventPtr->nextPtr = evPtr; 00468 } 00469 tsdPtr->lastEventPtr = evPtr; 00470 } else if (position == TCL_QUEUE_HEAD) { 00471 /* 00472 * Push the event on the head of the queue. 00473 */ 00474 00475 evPtr->nextPtr = tsdPtr->firstEventPtr; 00476 if (tsdPtr->firstEventPtr == NULL) { 00477 tsdPtr->lastEventPtr = evPtr; 00478 } 00479 tsdPtr->firstEventPtr = evPtr; 00480 } else if (position == TCL_QUEUE_MARK) { 00481 /* 00482 * Insert the event after the current marker event and advance the 00483 * marker to the new event. 00484 */ 00485 00486 if (tsdPtr->markerEventPtr == NULL) { 00487 evPtr->nextPtr = tsdPtr->firstEventPtr; 00488 tsdPtr->firstEventPtr = evPtr; 00489 } else { 00490 evPtr->nextPtr = tsdPtr->markerEventPtr->nextPtr; 00491 tsdPtr->markerEventPtr->nextPtr = evPtr; 00492 } 00493 tsdPtr->markerEventPtr = evPtr; 00494 if (evPtr->nextPtr == NULL) { 00495 tsdPtr->lastEventPtr = evPtr; 00496 } 00497 } 00498 Tcl_MutexUnlock(&(tsdPtr->queueMutex)); 00499 } 00500 00501 /* 00502 *---------------------------------------------------------------------- 00503 * 00504 * Tcl_DeleteEvents -- 00505 * 00506 * Calls a function for each event in the queue and deletes those for 00507 * which the function returns 1. Events for which the function returns 0 00508 * are left in the queue. Operates on the queue associated with the 00509 * current thread. 00510 * 00511 * Results: 00512 * None. 00513 * 00514 * Side effects: 00515 * Potentially removes one or more events from the event queue. 00516 * 00517 *---------------------------------------------------------------------- 00518 */ 00519 00520 void 00521 Tcl_DeleteEvents( 00522 Tcl_EventDeleteProc *proc, /* The function to call. */ 00523 ClientData clientData) /* The type-specific data. */ 00524 { 00525 Tcl_Event *evPtr; /* Pointer to the event being examined */ 00526 Tcl_Event *prevPtr; /* Pointer to evPtr's predecessor, or NULL if 00527 * evPtr designates the first event in the 00528 * queue for the thread. */ 00529 Tcl_Event* hold; 00530 00531 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00532 00533 Tcl_MutexLock(&(tsdPtr->queueMutex)); 00534 00535 /* 00536 * Walk the queue of events for the thread, applying 'proc' to each to 00537 * decide whether to eliminate the event. 00538 */ 00539 00540 prevPtr = NULL; 00541 evPtr = tsdPtr->firstEventPtr; 00542 while (evPtr != NULL) { 00543 if ((*proc)(evPtr, clientData) == 1) { 00544 /* 00545 * This event should be deleted. Unlink it. 00546 */ 00547 00548 if (prevPtr == NULL) { 00549 tsdPtr->firstEventPtr = evPtr->nextPtr; 00550 } else { 00551 prevPtr->nextPtr = evPtr->nextPtr; 00552 } 00553 00554 /* 00555 * Update 'last' and 'marker' events if either has been deleted. 00556 */ 00557 00558 if (evPtr->nextPtr == NULL) { 00559 tsdPtr->lastEventPtr = prevPtr; 00560 } 00561 if (tsdPtr->markerEventPtr == evPtr) { 00562 tsdPtr->markerEventPtr = prevPtr; 00563 } 00564 00565 /* 00566 * Delete the event data structure. 00567 */ 00568 00569 hold = evPtr; 00570 evPtr = evPtr->nextPtr; 00571 ckfree((char *) hold); 00572 } else { 00573 /* 00574 * Event is to be retained. 00575 */ 00576 00577 prevPtr = evPtr; 00578 evPtr = evPtr->nextPtr; 00579 } 00580 } 00581 Tcl_MutexUnlock(&(tsdPtr->queueMutex)); 00582 } 00583 00584 /* 00585 *---------------------------------------------------------------------- 00586 * 00587 * Tcl_ServiceEvent -- 00588 * 00589 * Process one event from the event queue, or invoke an asynchronous 00590 * event handler. Operates on event queue for current thread. 00591 * 00592 * Results: 00593 * The return value is 1 if the function actually found an event to 00594 * process. If no processing occurred, then 0 is returned. 00595 * 00596 * Side effects: 00597 * Invokes all of the event handlers for the highest priority event in 00598 * the event queue. May collapse some events into a single event or 00599 * discard stale events. 00600 * 00601 *---------------------------------------------------------------------- 00602 */ 00603 00604 int 00605 Tcl_ServiceEvent( 00606 int flags) /* Indicates what events should be processed. 00607 * May be any combination of TCL_WINDOW_EVENTS 00608 * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other 00609 * flags defined elsewhere. Events not 00610 * matching this will be skipped for 00611 * processing later. */ 00612 { 00613 Tcl_Event *evPtr, *prevPtr; 00614 Tcl_EventProc *proc; 00615 int result; 00616 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00617 00618 /* 00619 * Asynchronous event handlers are considered to be the highest priority 00620 * events, and so must be invoked before we process events on the event 00621 * queue. 00622 */ 00623 00624 if (Tcl_AsyncReady()) { 00625 (void) Tcl_AsyncInvoke(NULL, 0); 00626 return 1; 00627 } 00628 00629 /* 00630 * No event flags is equivalent to TCL_ALL_EVENTS. 00631 */ 00632 00633 if ((flags & TCL_ALL_EVENTS) == 0) { 00634 flags |= TCL_ALL_EVENTS; 00635 } 00636 00637 /* 00638 * Loop through all the events in the queue until we find one that can 00639 * actually be handled. 00640 */ 00641 00642 Tcl_MutexLock(&(tsdPtr->queueMutex)); 00643 for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL; 00644 evPtr = evPtr->nextPtr) { 00645 /* 00646 * Call the handler for the event. If it actually handles the event 00647 * then free the storage for the event. There are two tricky things 00648 * here, both stemming from the fact that the event code may be 00649 * re-entered while servicing the event: 00650 * 00651 * 1. Set the "proc" field to NULL. This is a signal to ourselves 00652 * that we shouldn't reexecute the handler if the event loop is 00653 * re-entered. 00654 * 2. When freeing the event, must search the queue again from the 00655 * front to find it. This is because the event queue could change 00656 * almost arbitrarily while handling the event, so we can't depend 00657 * on pointers found now still being valid when the handler 00658 * returns. 00659 */ 00660 00661 proc = evPtr->proc; 00662 if (proc == NULL) { 00663 continue; 00664 } 00665 evPtr->proc = NULL; 00666 00667 /* 00668 * Release the lock before calling the event function. This allows 00669 * other threads to post events if we enter a recursive event loop in 00670 * this thread. Note that we are making the assumption that if the 00671 * proc returns 0, the event is still in the list. 00672 */ 00673 00674 Tcl_MutexUnlock(&(tsdPtr->queueMutex)); 00675 result = (*proc)(evPtr, flags); 00676 Tcl_MutexLock(&(tsdPtr->queueMutex)); 00677 00678 if (result) { 00679 /* 00680 * The event was processed, so remove it from the queue. 00681 */ 00682 00683 if (tsdPtr->firstEventPtr == evPtr) { 00684 tsdPtr->firstEventPtr = evPtr->nextPtr; 00685 if (evPtr->nextPtr == NULL) { 00686 tsdPtr->lastEventPtr = NULL; 00687 } 00688 if (tsdPtr->markerEventPtr == evPtr) { 00689 tsdPtr->markerEventPtr = NULL; 00690 } 00691 } else { 00692 for (prevPtr = tsdPtr->firstEventPtr; 00693 prevPtr && prevPtr->nextPtr != evPtr; 00694 prevPtr = prevPtr->nextPtr) { 00695 /* Empty loop body. */ 00696 } 00697 if (prevPtr) { 00698 prevPtr->nextPtr = evPtr->nextPtr; 00699 if (evPtr->nextPtr == NULL) { 00700 tsdPtr->lastEventPtr = prevPtr; 00701 } 00702 if (tsdPtr->markerEventPtr == evPtr) { 00703 tsdPtr->markerEventPtr = prevPtr; 00704 } 00705 } else { 00706 evPtr = NULL; 00707 } 00708 } 00709 if (evPtr) { 00710 ckfree((char *) evPtr); 00711 } 00712 Tcl_MutexUnlock(&(tsdPtr->queueMutex)); 00713 return 1; 00714 } else { 00715 /* 00716 * The event wasn't actually handled, so we have to restore the 00717 * proc field to allow the event to be attempted again. 00718 */ 00719 00720 evPtr->proc = proc; 00721 } 00722 } 00723 Tcl_MutexUnlock(&(tsdPtr->queueMutex)); 00724 return 0; 00725 } 00726 00727 /* 00728 *---------------------------------------------------------------------- 00729 * 00730 * Tcl_GetServiceMode -- 00731 * 00732 * This routine returns the current service mode of the notifier. 00733 * 00734 * Results: 00735 * Returns either TCL_SERVICE_ALL or TCL_SERVICE_NONE. 00736 * 00737 * Side effects: 00738 * None. 00739 * 00740 *---------------------------------------------------------------------- 00741 */ 00742 00743 int 00744 Tcl_GetServiceMode(void) 00745 { 00746 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00747 00748 return tsdPtr->serviceMode; 00749 } 00750 00751 /* 00752 *---------------------------------------------------------------------- 00753 * 00754 * Tcl_SetServiceMode -- 00755 * 00756 * This routine sets the current service mode of the tsdPtr-> 00757 * 00758 * Results: 00759 * Returns the previous service mode. 00760 * 00761 * Side effects: 00762 * Invokes the notifier service mode hook function. 00763 * 00764 *---------------------------------------------------------------------- 00765 */ 00766 00767 int 00768 Tcl_SetServiceMode( 00769 int mode) /* New service mode: TCL_SERVICE_ALL or 00770 * TCL_SERVICE_NONE */ 00771 { 00772 int oldMode; 00773 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00774 00775 oldMode = tsdPtr->serviceMode; 00776 tsdPtr->serviceMode = mode; 00777 if (tclStubs.tcl_ServiceModeHook) { 00778 tclStubs.tcl_ServiceModeHook(mode); 00779 } 00780 return oldMode; 00781 } 00782 00783 /* 00784 *---------------------------------------------------------------------- 00785 * 00786 * Tcl_SetMaxBlockTime -- 00787 * 00788 * This function is invoked by event sources to tell the notifier how 00789 * long it may block the next time it blocks. The timePtr argument gives 00790 * a maximum time; the actual time may be less if some other event source 00791 * requested a smaller time. 00792 * 00793 * Results: 00794 * None. 00795 * 00796 * Side effects: 00797 * May reduce the length of the next sleep in the tsdPtr-> 00798 * 00799 *---------------------------------------------------------------------- 00800 */ 00801 00802 void 00803 Tcl_SetMaxBlockTime( 00804 Tcl_Time *timePtr) /* Specifies a maximum elapsed time for the 00805 * next blocking operation in the event 00806 * tsdPtr-> */ 00807 { 00808 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00809 00810 if (!tsdPtr->blockTimeSet || (timePtr->sec < tsdPtr->blockTime.sec) 00811 || ((timePtr->sec == tsdPtr->blockTime.sec) 00812 && (timePtr->usec < tsdPtr->blockTime.usec))) { 00813 tsdPtr->blockTime = *timePtr; 00814 tsdPtr->blockTimeSet = 1; 00815 } 00816 00817 /* 00818 * If we are called outside an event source traversal, set the timeout 00819 * immediately. 00820 */ 00821 00822 if (!tsdPtr->inTraversal) { 00823 if (tsdPtr->blockTimeSet) { 00824 Tcl_SetTimer(&tsdPtr->blockTime); 00825 } else { 00826 Tcl_SetTimer(NULL); 00827 } 00828 } 00829 } 00830 00831 /* 00832 *---------------------------------------------------------------------- 00833 * 00834 * Tcl_DoOneEvent -- 00835 * 00836 * Process a single event of some sort. If there's no work to do, wait 00837 * for an event to occur, then process it. 00838 * 00839 * Results: 00840 * The return value is 1 if the function actually found an event to 00841 * process. If no processing occurred, then 0 is returned (this can 00842 * happen if the TCL_DONT_WAIT flag is set or if there are no event 00843 * handlers to wait for in the set specified by flags). 00844 * 00845 * Side effects: 00846 * May delay execution of process while waiting for an event, unless 00847 * TCL_DONT_WAIT is set in the flags argument. Event sources are invoked 00848 * to check for and queue events. Event handlers may produce arbitrary 00849 * side effects. 00850 * 00851 *---------------------------------------------------------------------- 00852 */ 00853 00854 int 00855 Tcl_DoOneEvent( 00856 int flags) /* Miscellaneous flag values: may be any 00857 * combination of TCL_DONT_WAIT, 00858 * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS, 00859 * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or 00860 * others defined by event sources. */ 00861 { 00862 int result = 0, oldMode; 00863 EventSource *sourcePtr; 00864 Tcl_Time *timePtr; 00865 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 00866 00867 /* 00868 * The first thing we do is to service any asynchronous event handlers. 00869 */ 00870 00871 if (Tcl_AsyncReady()) { 00872 (void) Tcl_AsyncInvoke(NULL, 0); 00873 return 1; 00874 } 00875 00876 /* 00877 * No event flags is equivalent to TCL_ALL_EVENTS. 00878 */ 00879 00880 if ((flags & TCL_ALL_EVENTS) == 0) { 00881 flags |= TCL_ALL_EVENTS; 00882 } 00883 00884 /* 00885 * Set the service mode to none so notifier event routines won't try to 00886 * service events recursively. 00887 */ 00888 00889 oldMode = tsdPtr->serviceMode; 00890 tsdPtr->serviceMode = TCL_SERVICE_NONE; 00891 00892 /* 00893 * The core of this function is an infinite loop, even though we only 00894 * service one event. The reason for this is that we may be processing 00895 * events that don't do anything inside of Tcl. 00896 */ 00897 00898 while (1) { 00899 /* 00900 * If idle events are the only things to service, skip the main part 00901 * of the loop and go directly to handle idle events (i.e. don't wait 00902 * even if TCL_DONT_WAIT isn't set). 00903 */ 00904 00905 if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) { 00906 flags = TCL_IDLE_EVENTS | TCL_DONT_WAIT; 00907 goto idleEvents; 00908 } 00909 00910 /* 00911 * Ask Tcl to service a queued event, if there are any. 00912 */ 00913 00914 if (Tcl_ServiceEvent(flags)) { 00915 result = 1; 00916 break; 00917 } 00918 00919 /* 00920 * If TCL_DONT_WAIT is set, be sure to poll rather than blocking, 00921 * otherwise reset the block time to infinity. 00922 */ 00923 00924 if (flags & TCL_DONT_WAIT) { 00925 tsdPtr->blockTime.sec = 0; 00926 tsdPtr->blockTime.usec = 0; 00927 tsdPtr->blockTimeSet = 1; 00928 } else { 00929 tsdPtr->blockTimeSet = 0; 00930 } 00931 00932 /* 00933 * Set up all the event sources for new events. This will cause the 00934 * block time to be updated if necessary. 00935 */ 00936 00937 tsdPtr->inTraversal = 1; 00938 for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; 00939 sourcePtr = sourcePtr->nextPtr) { 00940 if (sourcePtr->setupProc) { 00941 (sourcePtr->setupProc)(sourcePtr->clientData, flags); 00942 } 00943 } 00944 tsdPtr->inTraversal = 0; 00945 00946 if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) { 00947 timePtr = &tsdPtr->blockTime; 00948 } else { 00949 timePtr = NULL; 00950 } 00951 00952 /* 00953 * Wait for a new event or a timeout. If Tcl_WaitForEvent returns -1, 00954 * we should abort Tcl_DoOneEvent. 00955 */ 00956 00957 result = Tcl_WaitForEvent(timePtr); 00958 if (result < 0) { 00959 result = 0; 00960 break; 00961 } 00962 00963 /* 00964 * Check all the event sources for new events. 00965 */ 00966 00967 for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; 00968 sourcePtr = sourcePtr->nextPtr) { 00969 if (sourcePtr->checkProc) { 00970 (sourcePtr->checkProc)(sourcePtr->clientData, flags); 00971 } 00972 } 00973 00974 /* 00975 * Check for events queued by the notifier or event sources. 00976 */ 00977 00978 if (Tcl_ServiceEvent(flags)) { 00979 result = 1; 00980 break; 00981 } 00982 00983 /* 00984 * We've tried everything at this point, but nobody we know about had 00985 * anything to do. Check for idle events. If none, either quit or go 00986 * back to the top and try again. 00987 */ 00988 00989 idleEvents: 00990 if (flags & TCL_IDLE_EVENTS) { 00991 if (TclServiceIdle()) { 00992 result = 1; 00993 break; 00994 } 00995 } 00996 if (flags & TCL_DONT_WAIT) { 00997 break; 00998 } 00999 01000 /* 01001 * If Tcl_WaitForEvent has returned 1, indicating that one system 01002 * event has been dispatched (and thus that some Tcl code might have 01003 * been indirectly executed), we break out of the loop. We do this to 01004 * give VwaitCmd for instance a chance to check if that system event 01005 * had the side effect of changing the variable (so the vwait can 01006 * return and unwind properly). 01007 * 01008 * NB: We will process idle events if any first, because otherwise we 01009 * might never do the idle events if the notifier always gets 01010 * system events. 01011 */ 01012 01013 if (result) { 01014 break; 01015 } 01016 } 01017 01018 tsdPtr->serviceMode = oldMode; 01019 return result; 01020 } 01021 01022 /* 01023 *---------------------------------------------------------------------- 01024 * 01025 * Tcl_ServiceAll -- 01026 * 01027 * This routine checks all of the event sources, processes events that 01028 * are on the Tcl event queue, and then calls the any idle handlers. 01029 * Platform specific notifier callbacks that generate events should call 01030 * this routine before returning to the system in order to ensure that 01031 * Tcl gets a chance to process the new events. 01032 * 01033 * Results: 01034 * Returns 1 if an event or idle handler was invoked, else 0. 01035 * 01036 * Side effects: 01037 * Anything that an event or idle handler may do. 01038 * 01039 *---------------------------------------------------------------------- 01040 */ 01041 01042 int 01043 Tcl_ServiceAll(void) 01044 { 01045 int result = 0; 01046 EventSource *sourcePtr; 01047 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 01048 01049 if (tsdPtr->serviceMode == TCL_SERVICE_NONE) { 01050 return result; 01051 } 01052 01053 /* 01054 * We need to turn off event servicing like we to in Tcl_DoOneEvent, to 01055 * avoid recursive calls. 01056 */ 01057 01058 tsdPtr->serviceMode = TCL_SERVICE_NONE; 01059 01060 /* 01061 * Check async handlers first. 01062 */ 01063 01064 if (Tcl_AsyncReady()) { 01065 (void) Tcl_AsyncInvoke(NULL, 0); 01066 } 01067 01068 /* 01069 * Make a single pass through all event sources, queued events, and idle 01070 * handlers. Note that we wait to update the notifier timer until the end 01071 * so we can avoid multiple changes. 01072 */ 01073 01074 tsdPtr->inTraversal = 1; 01075 tsdPtr->blockTimeSet = 0; 01076 01077 for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; 01078 sourcePtr = sourcePtr->nextPtr) { 01079 if (sourcePtr->setupProc) { 01080 (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS); 01081 } 01082 } 01083 for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; 01084 sourcePtr = sourcePtr->nextPtr) { 01085 if (sourcePtr->checkProc) { 01086 (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS); 01087 } 01088 } 01089 01090 while (Tcl_ServiceEvent(0)) { 01091 result = 1; 01092 } 01093 if (TclServiceIdle()) { 01094 result = 1; 01095 } 01096 01097 if (!tsdPtr->blockTimeSet) { 01098 Tcl_SetTimer(NULL); 01099 } else { 01100 Tcl_SetTimer(&tsdPtr->blockTime); 01101 } 01102 tsdPtr->inTraversal = 0; 01103 tsdPtr->serviceMode = TCL_SERVICE_ALL; 01104 return result; 01105 } 01106 01107 /* 01108 *---------------------------------------------------------------------- 01109 * 01110 * Tcl_ThreadAlert -- 01111 * 01112 * This function wakes up the notifier associated with the specified 01113 * thread (if there is one). 01114 * 01115 * Results: 01116 * None. 01117 * 01118 * Side effects: 01119 * None. 01120 * 01121 *---------------------------------------------------------------------- 01122 */ 01123 01124 void 01125 Tcl_ThreadAlert( 01126 Tcl_ThreadId threadId) /* Identifier for thread to use. */ 01127 { 01128 ThreadSpecificData *tsdPtr; 01129 01130 /* 01131 * Find the notifier associated with the specified thread. Note that we 01132 * need to hold the listLock while calling Tcl_AlertNotifier to avoid a 01133 * race condition where the specified thread might destroy its notifier. 01134 */ 01135 01136 Tcl_MutexLock(&listLock); 01137 for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { 01138 if (tsdPtr->threadId == threadId) { 01139 if (tclStubs.tcl_AlertNotifier) { 01140 tclStubs.tcl_AlertNotifier(tsdPtr->clientData); 01141 } 01142 break; 01143 } 01144 } 01145 Tcl_MutexUnlock(&listLock); 01146 } 01147 01148 /* 01149 * Local Variables: 01150 * mode: c 01151 * c-basic-offset: 4 01152 * fill-column: 78 01153 * End: 01154 */
Generated on Wed Mar 12 12:18:19 2008 by 1.5.1 |