Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclNotify.c @ 64

Last change on this file since 64 was 25, checked in by landauf, 16 years ago

added tcl to libs

File size: 31.1 KB
Line 
1/*
2 * tclNotify.c --
3 *
4 *      This file implements the generic portion of the Tcl notifier. The
5 *      notifier is lowest-level part of the event system. It manages an event
6 *      queue that holds Tcl_Event structures. The platform specific portion
7 *      of the notifier is defined in the tcl*Notify.c files in each platform
8 *      directory.
9 *
10 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
11 * Copyright (c) 1998 by Scriptics Corporation.
12 * Copyright (c) 2003 by Kevin B. Kenny.  All rights reserved.
13 *
14 * See the file "license.terms" for information on usage and redistribution of
15 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 *
17 * RCS: @(#) $Id: tclNotify.c,v 1.25 2006/09/25 15:02:54 dkf Exp $
18 */
19
20#include "tclInt.h"
21
22extern TclStubs tclStubs;
23
24/*
25 * For each event source (created with Tcl_CreateEventSource) there is a
26 * structure of the following type:
27 */
28
29typedef struct EventSource {
30    Tcl_EventSetupProc *setupProc;
31    Tcl_EventCheckProc *checkProc;
32    ClientData clientData;
33    struct EventSource *nextPtr;
34} EventSource;
35
36/*
37 * The following structure keeps track of the state of the notifier on a
38 * per-thread basis. The first three elements keep track of the event queue.
39 * In addition to the first (next to be serviced) and last events in the
40 * queue, we keep track of a "marker" event. This provides a simple priority
41 * mechanism whereby events can be inserted at the front of the queue but
42 * behind all other high-priority events already in the queue (this is used
43 * for things like a sequence of Enter and Leave events generated during a
44 * grab in Tk). These elements are protected by the queueMutex so that any
45 * thread can queue an event on any notifier. Note that all of the values in
46 * this structure will be initialized to 0.
47 */
48
49typedef struct ThreadSpecificData {
50    Tcl_Event *firstEventPtr;   /* First pending event, or NULL if none. */
51    Tcl_Event *lastEventPtr;    /* Last pending event, or NULL if none. */
52    Tcl_Event *markerEventPtr;  /* Last high-priority event in queue, or NULL
53                                 * if none. */
54    Tcl_Mutex queueMutex;       /* Mutex to protect access to the previous
55                                 * three fields. */
56    int serviceMode;            /* One of TCL_SERVICE_NONE or
57                                 * TCL_SERVICE_ALL. */
58    int blockTimeSet;           /* 0 means there is no maximum block time:
59                                 * block forever. */
60    Tcl_Time blockTime;         /* If blockTimeSet is 1, gives the maximum
61                                 * elapsed time for the next block. */
62    int inTraversal;            /* 1 if Tcl_SetMaxBlockTime is being called
63                                 * during an event source traversal. */
64    EventSource *firstEventSourcePtr;
65                                /* Pointer to first event source in list of
66                                 * event sources for this thread. */
67    Tcl_ThreadId threadId;      /* Thread that owns this notifier instance. */
68    ClientData clientData;      /* Opaque handle for platform specific
69                                 * notifier. */
70    int initialized;            /* 1 if notifier has been initialized. */
71    struct ThreadSpecificData *nextPtr;
72                                /* Next notifier in global list of notifiers.
73                                 * Access is controlled by the listLock global
74                                 * mutex. */
75} ThreadSpecificData;
76
77static Tcl_ThreadDataKey dataKey;
78
79/*
80 * Global list of notifiers. Access to this list is controlled by the listLock
81 * mutex. If this becomes a performance bottleneck, this could be replaced
82 * with a hashtable.
83 */
84
85static ThreadSpecificData *firstNotifierPtr = NULL;
86TCL_DECLARE_MUTEX(listLock)
87
88/*
89 * Declarations for routines used only in this file.
90 */
91
92static void             QueueEvent(ThreadSpecificData *tsdPtr,
93                            Tcl_Event* evPtr, Tcl_QueuePosition position);
94
95/*
96 *----------------------------------------------------------------------
97 *
98 * TclInitNotifier --
99 *
100 *      Initialize the thread local data structures for the notifier
101 *      subsystem.
102 *
103 * Results:
104 *      None.
105 *
106 * Side effects:
107 *      Adds the current thread to the global list of notifiers.
108 *
109 *----------------------------------------------------------------------
110 */
111
112void
113TclInitNotifier(void)
114{
115    ThreadSpecificData *tsdPtr;
116    Tcl_ThreadId threadId = Tcl_GetCurrentThread();
117
118    Tcl_MutexLock(&listLock);
119    for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;
120            tsdPtr = tsdPtr->nextPtr) {
121        /* Empty loop body. */
122    }
123
124    if (NULL == tsdPtr) {
125        /*
126         * Notifier not yet initialized in this thread.
127         */
128
129        tsdPtr = TCL_TSD_INIT(&dataKey);
130        tsdPtr->threadId = threadId;
131        tsdPtr->clientData = tclStubs.tcl_InitNotifier();
132        tsdPtr->initialized = 1;
133        tsdPtr->nextPtr = firstNotifierPtr;
134        firstNotifierPtr = tsdPtr;
135    }
136    Tcl_MutexUnlock(&listLock);
137}
138
139/*
140 *----------------------------------------------------------------------
141 *
142 * TclFinalizeNotifier --
143 *
144 *      Finalize the thread local data structures for the notifier subsystem.
145 *
146 * Results:
147 *      None.
148 *
149 * Side effects:
150 *      Removes the notifier associated with the current thread from the
151 *      global notifier list. This is done only if the notifier was
152 *      initialized for this thread by call to TclInitNotifier(). This is
153 *      always true for threads which have been seeded with an Tcl
154 *      interpreter, since the call to Tcl_CreateInterp will, among other
155 *      things, call TclInitializeSubsystems() and this one will, in turn,
156 *      call the TclInitNotifier() for the thread. For threads created without
157 *      the Tcl interpreter, though, nobody is explicitly nor implicitly
158 *      calling the TclInitNotifier hence, TclFinalizeNotifier should not be
159 *      performed at all.
160 *
161 *----------------------------------------------------------------------
162 */
163
164void
165TclFinalizeNotifier(void)
166{
167    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
168    ThreadSpecificData **prevPtrPtr;
169    Tcl_Event *evPtr, *hold;
170
171    if (!tsdPtr->initialized) {
172        return;         /* Notifier not initialized for the current thread */
173    }
174
175    Tcl_MutexLock(&(tsdPtr->queueMutex));
176    for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL; ) {
177        hold = evPtr;
178        evPtr = evPtr->nextPtr;
179        ckfree((char *) hold);
180    }
181    tsdPtr->firstEventPtr = NULL;
182    tsdPtr->lastEventPtr = NULL;
183    Tcl_MutexUnlock(&(tsdPtr->queueMutex));
184
185    Tcl_MutexLock(&listLock);
186
187    if (tclStubs.tcl_FinalizeNotifier) {
188        tclStubs.tcl_FinalizeNotifier(tsdPtr->clientData);
189    }
190    Tcl_MutexFinalize(&(tsdPtr->queueMutex));
191    for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;
192            prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {
193        if (*prevPtrPtr == tsdPtr) {
194            *prevPtrPtr = tsdPtr->nextPtr;
195            break;
196        }
197    }
198    tsdPtr->initialized = 0;
199
200    Tcl_MutexUnlock(&listLock);
201}
202
203/*
204 *----------------------------------------------------------------------
205 *
206 * Tcl_SetNotifier --
207 *
208 *      Install a set of alternate functions for use with the notifier. In
209 *      particular, this can be used to install the Xt-based notifier for use
210 *      with the Browser plugin.
211 *
212 * Results:
213 *      None.
214 *
215 * Side effects:
216 *      Overstomps part of the stub vector. This relies on hooks added to the
217 *      default functions in case those are called directly (i.e., not through
218 *      the stub table.)
219 *
220 *----------------------------------------------------------------------
221 */
222
223void
224Tcl_SetNotifier(
225    Tcl_NotifierProcs *notifierProcPtr)
226{
227#if !defined(__WIN32__) /* UNIX */
228    tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc;
229    tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc;
230#endif
231    tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;
232    tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;
233    tclStubs.tcl_InitNotifier = notifierProcPtr->initNotifierProc;
234    tclStubs.tcl_FinalizeNotifier = notifierProcPtr->finalizeNotifierProc;
235    tclStubs.tcl_AlertNotifier = notifierProcPtr->alertNotifierProc;
236    tclStubs.tcl_ServiceModeHook = notifierProcPtr->serviceModeHookProc;
237}
238
239/*
240 *----------------------------------------------------------------------
241 *
242 * Tcl_CreateEventSource --
243 *
244 *      This function is invoked to create a new source of events. The source
245 *      is identified by a function that gets invoked during Tcl_DoOneEvent to
246 *      check for events on that source and queue them.
247 *
248 *
249 * Results:
250 *      None.
251 *
252 * Side effects:
253 *      SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
254 *      runs out of things to do. SetupProc will be invoked before
255 *      Tcl_DoOneEvent calls select or whatever else it uses to wait for
256 *      events. SetupProc typically calls functions like Tcl_SetMaxBlockTime
257 *      to indicate what to wait for.
258 *
259 *      CheckProc is called after select or whatever operation was actually
260 *      used to wait. It figures out whether anything interesting actually
261 *      happened (e.g. by calling Tcl_AsyncReady), and then calls
262 *      Tcl_QueueEvent to queue any events that are ready.
263 *
264 *      Each of these functions is passed two arguments, e.g.
265 *              (*checkProc)(ClientData clientData, int flags));
266 *      ClientData is the same as the clientData argument here, and flags is a
267 *      combination of things like TCL_FILE_EVENTS that indicates what events
268 *      are of interest: setupProc and checkProc use flags to figure out
269 *      whether their events are relevant or not.
270 *
271 *----------------------------------------------------------------------
272 */
273
274void
275Tcl_CreateEventSource(
276    Tcl_EventSetupProc *setupProc,
277                                /* Function to invoke to figure out what to
278                                 * wait for. */
279    Tcl_EventCheckProc *checkProc,
280                                /* Function to call after waiting to see what
281                                 * happened. */
282    ClientData clientData)      /* One-word argument to pass to setupProc and
283                                 * checkProc. */
284{
285    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
286    EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
287
288    sourcePtr->setupProc = setupProc;
289    sourcePtr->checkProc = checkProc;
290    sourcePtr->clientData = clientData;
291    sourcePtr->nextPtr = tsdPtr->firstEventSourcePtr;
292    tsdPtr->firstEventSourcePtr = sourcePtr;
293}
294
295/*
296 *----------------------------------------------------------------------
297 *
298 * Tcl_DeleteEventSource --
299 *
300 *      This function is invoked to delete the source of events given by proc
301 *      and clientData.
302 *
303 * Results:
304 *      None.
305 *
306 * Side effects:
307 *      The given event source is cancelled, so its function will never again
308 *      be called. If no such source exists, nothing happens.
309 *
310 *----------------------------------------------------------------------
311 */
312
313void
314Tcl_DeleteEventSource(
315    Tcl_EventSetupProc *setupProc,
316                                /* Function to invoke to figure out what to
317                                 * wait for. */
318    Tcl_EventCheckProc *checkProc,
319                                /* Function to call after waiting to see what
320                                 * happened. */
321    ClientData clientData)      /* One-word argument to pass to setupProc and
322                                 * checkProc. */
323{
324    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
325    EventSource *sourcePtr, *prevPtr;
326
327    for (sourcePtr = tsdPtr->firstEventSourcePtr, prevPtr = NULL;
328            sourcePtr != NULL;
329            prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
330        if ((sourcePtr->setupProc != setupProc)
331                || (sourcePtr->checkProc != checkProc)
332                || (sourcePtr->clientData != clientData)) {
333            continue;
334        }
335        if (prevPtr == NULL) {
336            tsdPtr->firstEventSourcePtr = sourcePtr->nextPtr;
337        } else {
338            prevPtr->nextPtr = sourcePtr->nextPtr;
339        }
340        ckfree((char *) sourcePtr);
341        return;
342    }
343}
344
345/*
346 *----------------------------------------------------------------------
347 *
348 * Tcl_QueueEvent --
349 *
350 *      Queue an event on the event queue associated with the current thread.
351 *
352 * Results:
353 *      None.
354 *
355 * Side effects:
356 *      None.
357 *
358 *----------------------------------------------------------------------
359 */
360
361void
362Tcl_QueueEvent(
363    Tcl_Event* evPtr,           /* Event to add to queue. The storage space
364                                 * must have been allocated the caller with
365                                 * malloc (ckalloc), and it becomes the
366                                 * property of the event queue. It will be
367                                 * freed after the event has been handled. */
368    Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
369                                 * TCL_QUEUE_MARK. */
370{
371    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
372    QueueEvent(tsdPtr, evPtr, position);
373}
374
375/*
376 *----------------------------------------------------------------------
377 *
378 * Tcl_ThreadQueueEvent --
379 *
380 *      Queue an event on the specified thread's event queue.
381 *
382 * Results:
383 *      None.
384 *
385 * Side effects:
386 *      None.
387 *
388 *----------------------------------------------------------------------
389 */
390
391void
392Tcl_ThreadQueueEvent(
393    Tcl_ThreadId threadId,      /* Identifier for thread to use. */
394    Tcl_Event *evPtr,           /* Event to add to queue. The storage space
395                                 * must have been allocated the caller with
396                                 * malloc (ckalloc), and it becomes the
397                                 * property of the event queue. It will be
398                                 * freed after the event has been handled. */
399    Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
400                                 * TCL_QUEUE_MARK. */
401{
402    ThreadSpecificData *tsdPtr;
403
404    /*
405     * Find the notifier associated with the specified thread.
406     */
407
408    Tcl_MutexLock(&listLock);
409    for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;
410            tsdPtr = tsdPtr->nextPtr) {
411        /* Empty loop body. */
412    }
413
414    /*
415     * Queue the event if there was a notifier associated with the thread.
416     */
417
418    if (tsdPtr) {
419        QueueEvent(tsdPtr, evPtr, position);
420    }
421    Tcl_MutexUnlock(&listLock);
422}
423
424/*
425 *----------------------------------------------------------------------
426 *
427 * QueueEvent --
428 *
429 *      Insert an event into the specified thread's event queue at one of
430 *      three positions: the head, the tail, or before a floating marker.
431 *      Events inserted before the marker will be processed in first-in-
432 *      first-out order, but before any events inserted at the tail of the
433 *      queue. Events inserted at the head of the queue will be processed in
434 *      last-in-first-out order.
435 *
436 * Results:
437 *      None.
438 *
439 * Side effects:
440 *      None.
441 *
442 *----------------------------------------------------------------------
443 */
444
445static void
446QueueEvent(
447    ThreadSpecificData *tsdPtr, /* Handle to thread local data that indicates
448                                 * which event queue to use. */
449    Tcl_Event *evPtr,           /* Event to add to queue.  The storage space
450                                 * must have been allocated the caller with
451                                 * malloc (ckalloc), and it becomes the
452                                 * property of the event queue. It will be
453                                 * freed after the event has been handled. */
454    Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
455                                 * TCL_QUEUE_MARK. */
456{
457    Tcl_MutexLock(&(tsdPtr->queueMutex));
458    if (position == TCL_QUEUE_TAIL) {
459        /*
460         * Append the event on the end of the queue.
461         */
462
463        evPtr->nextPtr = NULL;
464        if (tsdPtr->firstEventPtr == NULL) {
465            tsdPtr->firstEventPtr = evPtr;
466        } else {
467            tsdPtr->lastEventPtr->nextPtr = evPtr;
468        }
469        tsdPtr->lastEventPtr = evPtr;
470    } else if (position == TCL_QUEUE_HEAD) {
471        /*
472         * Push the event on the head of the queue.
473         */
474
475        evPtr->nextPtr = tsdPtr->firstEventPtr;
476        if (tsdPtr->firstEventPtr == NULL) {
477            tsdPtr->lastEventPtr = evPtr;
478        }
479        tsdPtr->firstEventPtr = evPtr;
480    } else if (position == TCL_QUEUE_MARK) {
481        /*
482         * Insert the event after the current marker event and advance the
483         * marker to the new event.
484         */
485
486        if (tsdPtr->markerEventPtr == NULL) {
487            evPtr->nextPtr = tsdPtr->firstEventPtr;
488            tsdPtr->firstEventPtr = evPtr;
489        } else {
490            evPtr->nextPtr = tsdPtr->markerEventPtr->nextPtr;
491            tsdPtr->markerEventPtr->nextPtr = evPtr;
492        }
493        tsdPtr->markerEventPtr = evPtr;
494        if (evPtr->nextPtr == NULL) {
495            tsdPtr->lastEventPtr = evPtr;
496        }
497    }
498    Tcl_MutexUnlock(&(tsdPtr->queueMutex));
499}
500
501/*
502 *----------------------------------------------------------------------
503 *
504 * Tcl_DeleteEvents --
505 *
506 *      Calls a function for each event in the queue and deletes those for
507 *      which the function returns 1. Events for which the function returns 0
508 *      are left in the queue. Operates on the queue associated with the
509 *      current thread.
510 *
511 * Results:
512 *      None.
513 *
514 * Side effects:
515 *      Potentially removes one or more events from the event queue.
516 *
517 *----------------------------------------------------------------------
518 */
519
520void
521Tcl_DeleteEvents(
522    Tcl_EventDeleteProc *proc,  /* The function to call. */
523    ClientData clientData)      /* The type-specific data. */
524{
525    Tcl_Event *evPtr;           /* Pointer to the event being examined */
526    Tcl_Event *prevPtr;         /* Pointer to evPtr's predecessor, or NULL if
527                                 * evPtr designates the first event in the
528                                 * queue for the thread. */
529    Tcl_Event* hold;
530
531    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
532
533    Tcl_MutexLock(&(tsdPtr->queueMutex));
534
535    /*
536     * Walk the queue of events for the thread, applying 'proc' to each to
537     * decide whether to eliminate the event.
538     */
539
540    prevPtr = NULL;
541    evPtr = tsdPtr->firstEventPtr;
542    while (evPtr != NULL) {
543        if ((*proc)(evPtr, clientData) == 1) {
544            /*
545             * This event should be deleted. Unlink it.
546             */
547
548            if (prevPtr == NULL) {
549                tsdPtr->firstEventPtr = evPtr->nextPtr;
550            } else {
551                prevPtr->nextPtr = evPtr->nextPtr;
552            }
553
554            /*
555             * Update 'last' and 'marker' events if either has been deleted.
556             */
557
558            if (evPtr->nextPtr == NULL) {
559                tsdPtr->lastEventPtr = prevPtr;
560            }
561            if (tsdPtr->markerEventPtr == evPtr) {
562                tsdPtr->markerEventPtr = prevPtr;
563            }
564
565            /*
566             * Delete the event data structure.
567             */
568
569            hold = evPtr;
570            evPtr = evPtr->nextPtr;
571            ckfree((char *) hold);
572        } else {
573            /*
574             * Event is to be retained.
575             */
576
577            prevPtr = evPtr;
578            evPtr = evPtr->nextPtr;
579        }
580    }
581    Tcl_MutexUnlock(&(tsdPtr->queueMutex));
582}
583
584/*
585 *----------------------------------------------------------------------
586 *
587 * Tcl_ServiceEvent --
588 *
589 *      Process one event from the event queue, or invoke an asynchronous
590 *      event handler. Operates on event queue for current thread.
591 *
592 * Results:
593 *      The return value is 1 if the function actually found an event to
594 *      process. If no processing occurred, then 0 is returned.
595 *
596 * Side effects:
597 *      Invokes all of the event handlers for the highest priority event in
598 *      the event queue. May collapse some events into a single event or
599 *      discard stale events.
600 *
601 *----------------------------------------------------------------------
602 */
603
604int
605Tcl_ServiceEvent(
606    int flags)                  /* Indicates what events should be processed.
607                                 * May be any combination of TCL_WINDOW_EVENTS
608                                 * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
609                                 * flags defined elsewhere. Events not
610                                 * matching this will be skipped for
611                                 * processing later. */
612{
613    Tcl_Event *evPtr, *prevPtr;
614    Tcl_EventProc *proc;
615    int result;
616    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
617
618    /*
619     * Asynchronous event handlers are considered to be the highest priority
620     * events, and so must be invoked before we process events on the event
621     * queue.
622     */
623
624    if (Tcl_AsyncReady()) {
625        (void) Tcl_AsyncInvoke(NULL, 0);
626        return 1;
627    }
628
629    /*
630     * No event flags is equivalent to TCL_ALL_EVENTS.
631     */
632
633    if ((flags & TCL_ALL_EVENTS) == 0) {
634        flags |= TCL_ALL_EVENTS;
635    }
636
637    /*
638     * Loop through all the events in the queue until we find one that can
639     * actually be handled.
640     */
641
642    Tcl_MutexLock(&(tsdPtr->queueMutex));
643    for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL;
644            evPtr = evPtr->nextPtr) {
645        /*
646         * Call the handler for the event. If it actually handles the event
647         * then free the storage for the event. There are two tricky things
648         * here, both stemming from the fact that the event code may be
649         * re-entered while servicing the event:
650         *
651         * 1. Set the "proc" field to NULL.  This is a signal to ourselves
652         *    that we shouldn't reexecute the handler if the event loop is
653         *    re-entered.
654         * 2. When freeing the event, must search the queue again from the
655         *    front to find it. This is because the event queue could change
656         *    almost arbitrarily while handling the event, so we can't depend
657         *    on pointers found now still being valid when the handler
658         *    returns.
659         */
660
661        proc = evPtr->proc;
662        if (proc == NULL) {
663            continue;
664        }
665        evPtr->proc = NULL;
666
667        /*
668         * Release the lock before calling the event function. This allows
669         * other threads to post events if we enter a recursive event loop in
670         * this thread. Note that we are making the assumption that if the
671         * proc returns 0, the event is still in the list.
672         */
673
674        Tcl_MutexUnlock(&(tsdPtr->queueMutex));
675        result = (*proc)(evPtr, flags);
676        Tcl_MutexLock(&(tsdPtr->queueMutex));
677
678        if (result) {
679            /*
680             * The event was processed, so remove it from the queue.
681             */
682
683            if (tsdPtr->firstEventPtr == evPtr) {
684                tsdPtr->firstEventPtr = evPtr->nextPtr;
685                if (evPtr->nextPtr == NULL) {
686                    tsdPtr->lastEventPtr = NULL;
687                }
688                if (tsdPtr->markerEventPtr == evPtr) {
689                    tsdPtr->markerEventPtr = NULL;
690                }
691            } else {
692                for (prevPtr = tsdPtr->firstEventPtr;
693                        prevPtr && prevPtr->nextPtr != evPtr;
694                        prevPtr = prevPtr->nextPtr) {
695                    /* Empty loop body. */
696                }
697                if (prevPtr) {
698                    prevPtr->nextPtr = evPtr->nextPtr;
699                    if (evPtr->nextPtr == NULL) {
700                        tsdPtr->lastEventPtr = prevPtr;
701                    }
702                    if (tsdPtr->markerEventPtr == evPtr) {
703                        tsdPtr->markerEventPtr = prevPtr;
704                    }
705                } else {
706                    evPtr = NULL;
707                }
708            }
709            if (evPtr) {
710                ckfree((char *) evPtr);
711            }
712            Tcl_MutexUnlock(&(tsdPtr->queueMutex));
713            return 1;
714        } else {
715            /*
716             * The event wasn't actually handled, so we have to restore the
717             * proc field to allow the event to be attempted again.
718             */
719
720            evPtr->proc = proc;
721        }
722    }
723    Tcl_MutexUnlock(&(tsdPtr->queueMutex));
724    return 0;
725}
726
727/*
728 *----------------------------------------------------------------------
729 *
730 * Tcl_GetServiceMode --
731 *
732 *      This routine returns the current service mode of the notifier.
733 *
734 * Results:
735 *      Returns either TCL_SERVICE_ALL or TCL_SERVICE_NONE.
736 *
737 * Side effects:
738 *      None.
739 *
740 *----------------------------------------------------------------------
741 */
742
743int
744Tcl_GetServiceMode(void)
745{
746    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
747
748    return tsdPtr->serviceMode;
749}
750
751/*
752 *----------------------------------------------------------------------
753 *
754 * Tcl_SetServiceMode --
755 *
756 *      This routine sets the current service mode of the tsdPtr->
757 *
758 * Results:
759 *      Returns the previous service mode.
760 *
761 * Side effects:
762 *      Invokes the notifier service mode hook function.
763 *
764 *----------------------------------------------------------------------
765 */
766
767int
768Tcl_SetServiceMode(
769    int mode)                   /* New service mode: TCL_SERVICE_ALL or
770                                 * TCL_SERVICE_NONE */
771{
772    int oldMode;
773    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
774
775    oldMode = tsdPtr->serviceMode;
776    tsdPtr->serviceMode = mode;
777    if (tclStubs.tcl_ServiceModeHook) {
778        tclStubs.tcl_ServiceModeHook(mode);
779    }
780    return oldMode;
781}
782
783/*
784 *----------------------------------------------------------------------
785 *
786 * Tcl_SetMaxBlockTime --
787 *
788 *      This function is invoked by event sources to tell the notifier how
789 *      long it may block the next time it blocks. The timePtr argument gives
790 *      a maximum time; the actual time may be less if some other event source
791 *      requested a smaller time.
792 *
793 * Results:
794 *      None.
795 *
796 * Side effects:
797 *      May reduce the length of the next sleep in the tsdPtr->
798 *
799 *----------------------------------------------------------------------
800 */
801
802void
803Tcl_SetMaxBlockTime(
804    Tcl_Time *timePtr)          /* Specifies a maximum elapsed time for the
805                                 * next blocking operation in the event
806                                 * tsdPtr-> */
807{
808    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
809
810    if (!tsdPtr->blockTimeSet || (timePtr->sec < tsdPtr->blockTime.sec)
811            || ((timePtr->sec == tsdPtr->blockTime.sec)
812            && (timePtr->usec < tsdPtr->blockTime.usec))) {
813        tsdPtr->blockTime = *timePtr;
814        tsdPtr->blockTimeSet = 1;
815    }
816
817    /*
818     * If we are called outside an event source traversal, set the timeout
819     * immediately.
820     */
821
822    if (!tsdPtr->inTraversal) {
823        if (tsdPtr->blockTimeSet) {
824            Tcl_SetTimer(&tsdPtr->blockTime);
825        } else {
826            Tcl_SetTimer(NULL);
827        }
828    }
829}
830
831/*
832 *----------------------------------------------------------------------
833 *
834 * Tcl_DoOneEvent --
835 *
836 *      Process a single event of some sort. If there's no work to do, wait
837 *      for an event to occur, then process it.
838 *
839 * Results:
840 *      The return value is 1 if the function actually found an event to
841 *      process. If no processing occurred, then 0 is returned (this can
842 *      happen if the TCL_DONT_WAIT flag is set or if there are no event
843 *      handlers to wait for in the set specified by flags).
844 *
845 * Side effects:
846 *      May delay execution of process while waiting for an event, unless
847 *      TCL_DONT_WAIT is set in the flags argument. Event sources are invoked
848 *      to check for and queue events. Event handlers may produce arbitrary
849 *      side effects.
850 *
851 *----------------------------------------------------------------------
852 */
853
854int
855Tcl_DoOneEvent(
856    int flags)                  /* Miscellaneous flag values: may be any
857                                 * combination of TCL_DONT_WAIT,
858                                 * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
859                                 * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
860                                 * others defined by event sources. */
861{
862    int result = 0, oldMode;
863    EventSource *sourcePtr;
864    Tcl_Time *timePtr;
865    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
866
867    /*
868     * The first thing we do is to service any asynchronous event handlers.
869     */
870
871    if (Tcl_AsyncReady()) {
872        (void) Tcl_AsyncInvoke(NULL, 0);
873        return 1;
874    }
875
876    /*
877     * No event flags is equivalent to TCL_ALL_EVENTS.
878     */
879
880    if ((flags & TCL_ALL_EVENTS) == 0) {
881        flags |= TCL_ALL_EVENTS;
882    }
883
884    /*
885     * Set the service mode to none so notifier event routines won't try to
886     * service events recursively.
887     */
888
889    oldMode = tsdPtr->serviceMode;
890    tsdPtr->serviceMode = TCL_SERVICE_NONE;
891
892    /*
893     * The core of this function is an infinite loop, even though we only
894     * service one event. The reason for this is that we may be processing
895     * events that don't do anything inside of Tcl.
896     */
897
898    while (1) {
899        /*
900         * If idle events are the only things to service, skip the main part
901         * of the loop and go directly to handle idle events (i.e. don't wait
902         * even if TCL_DONT_WAIT isn't set).
903         */
904
905        if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {
906            flags = TCL_IDLE_EVENTS | TCL_DONT_WAIT;
907            goto idleEvents;
908        }
909
910        /*
911         * Ask Tcl to service a queued event, if there are any.
912         */
913
914        if (Tcl_ServiceEvent(flags)) {
915            result = 1;
916            break;
917        }
918
919        /*
920         * If TCL_DONT_WAIT is set, be sure to poll rather than blocking,
921         * otherwise reset the block time to infinity.
922         */
923
924        if (flags & TCL_DONT_WAIT) {
925            tsdPtr->blockTime.sec = 0;
926            tsdPtr->blockTime.usec = 0;
927            tsdPtr->blockTimeSet = 1;
928        } else {
929            tsdPtr->blockTimeSet = 0;
930        }
931
932        /*
933         * Set up all the event sources for new events. This will cause the
934         * block time to be updated if necessary.
935         */
936
937        tsdPtr->inTraversal = 1;
938        for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
939                sourcePtr = sourcePtr->nextPtr) {
940            if (sourcePtr->setupProc) {
941                (sourcePtr->setupProc)(sourcePtr->clientData, flags);
942            }
943        }
944        tsdPtr->inTraversal = 0;
945
946        if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) {
947            timePtr = &tsdPtr->blockTime;
948        } else {
949            timePtr = NULL;
950        }
951
952        /*
953         * Wait for a new event or a timeout. If Tcl_WaitForEvent returns -1,
954         * we should abort Tcl_DoOneEvent.
955         */
956
957        result = Tcl_WaitForEvent(timePtr);
958        if (result < 0) {
959            result = 0;
960            break;
961        }
962
963        /*
964         * Check all the event sources for new events.
965         */
966
967        for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
968                sourcePtr = sourcePtr->nextPtr) {
969            if (sourcePtr->checkProc) {
970                (sourcePtr->checkProc)(sourcePtr->clientData, flags);
971            }
972        }
973
974        /*
975         * Check for events queued by the notifier or event sources.
976         */
977
978        if (Tcl_ServiceEvent(flags)) {
979            result = 1;
980            break;
981        }
982
983        /*
984         * We've tried everything at this point, but nobody we know about had
985         * anything to do. Check for idle events. If none, either quit or go
986         * back to the top and try again.
987         */
988
989    idleEvents:
990        if (flags & TCL_IDLE_EVENTS) {
991            if (TclServiceIdle()) {
992                result = 1;
993                break;
994            }
995        }
996        if (flags & TCL_DONT_WAIT) {
997            break;
998        }
999
1000        /*
1001         * If Tcl_WaitForEvent has returned 1, indicating that one system
1002         * event has been dispatched (and thus that some Tcl code might have
1003         * been indirectly executed), we break out of the loop. We do this to
1004         * give VwaitCmd for instance a chance to check if that system event
1005         * had the side effect of changing the variable (so the vwait can
1006         * return and unwind properly).
1007         *
1008         * NB: We will process idle events if any first, because otherwise we
1009         *     might never do the idle events if the notifier always gets
1010         *     system events.
1011         */
1012
1013        if (result) {
1014            break;
1015        }
1016    }
1017
1018    tsdPtr->serviceMode = oldMode;
1019    return result;
1020}
1021
1022/*
1023 *----------------------------------------------------------------------
1024 *
1025 * Tcl_ServiceAll --
1026 *
1027 *      This routine checks all of the event sources, processes events that
1028 *      are on the Tcl event queue, and then calls the any idle handlers.
1029 *      Platform specific notifier callbacks that generate events should call
1030 *      this routine before returning to the system in order to ensure that
1031 *      Tcl gets a chance to process the new events.
1032 *
1033 * Results:
1034 *      Returns 1 if an event or idle handler was invoked, else 0.
1035 *
1036 * Side effects:
1037 *      Anything that an event or idle handler may do.
1038 *
1039 *----------------------------------------------------------------------
1040 */
1041
1042int
1043Tcl_ServiceAll(void)
1044{
1045    int result = 0;
1046    EventSource *sourcePtr;
1047    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1048
1049    if (tsdPtr->serviceMode == TCL_SERVICE_NONE) {
1050        return result;
1051    }
1052
1053    /*
1054     * We need to turn off event servicing like we to in Tcl_DoOneEvent, to
1055     * avoid recursive calls.
1056     */
1057
1058    tsdPtr->serviceMode = TCL_SERVICE_NONE;
1059
1060    /*
1061     * Check async handlers first.
1062     */
1063
1064    if (Tcl_AsyncReady()) {
1065        (void) Tcl_AsyncInvoke(NULL, 0);
1066    }
1067
1068    /*
1069     * Make a single pass through all event sources, queued events, and idle
1070     * handlers. Note that we wait to update the notifier timer until the end
1071     * so we can avoid multiple changes.
1072     */
1073
1074    tsdPtr->inTraversal = 1;
1075    tsdPtr->blockTimeSet = 0;
1076
1077    for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1078            sourcePtr = sourcePtr->nextPtr) {
1079        if (sourcePtr->setupProc) {
1080            (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
1081        }
1082    }
1083    for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1084            sourcePtr = sourcePtr->nextPtr) {
1085        if (sourcePtr->checkProc) {
1086            (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
1087        }
1088    }
1089
1090    while (Tcl_ServiceEvent(0)) {
1091        result = 1;
1092    }
1093    if (TclServiceIdle()) {
1094        result = 1;
1095    }
1096
1097    if (!tsdPtr->blockTimeSet) {
1098        Tcl_SetTimer(NULL);
1099    } else {
1100        Tcl_SetTimer(&tsdPtr->blockTime);
1101    }
1102    tsdPtr->inTraversal = 0;
1103    tsdPtr->serviceMode = TCL_SERVICE_ALL;
1104    return result;
1105}
1106
1107/*
1108 *----------------------------------------------------------------------
1109 *
1110 * Tcl_ThreadAlert --
1111 *
1112 *      This function wakes up the notifier associated with the specified
1113 *      thread (if there is one).
1114 *
1115 * Results:
1116 *      None.
1117 *
1118 * Side effects:
1119 *      None.
1120 *
1121 *----------------------------------------------------------------------
1122 */
1123
1124void
1125Tcl_ThreadAlert(
1126    Tcl_ThreadId threadId)      /* Identifier for thread to use. */
1127{
1128    ThreadSpecificData *tsdPtr;
1129
1130    /*
1131     * Find the notifier associated with the specified thread. Note that we
1132     * need to hold the listLock while calling Tcl_AlertNotifier to avoid a
1133     * race condition where the specified thread might destroy its notifier.
1134     */
1135
1136    Tcl_MutexLock(&listLock);
1137    for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1138        if (tsdPtr->threadId == threadId) {
1139            if (tclStubs.tcl_AlertNotifier) {
1140                tclStubs.tcl_AlertNotifier(tsdPtr->clientData);
1141            }
1142            break;
1143        }
1144    }
1145    Tcl_MutexUnlock(&listLock);
1146}
1147
1148/*
1149 * Local Variables:
1150 * mode: c
1151 * c-basic-offset: 4
1152 * fill-column: 78
1153 * End:
1154 */
Note: See TracBrowser for help on using the repository browser.