/[dtapublic]/projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclnotify.c
ViewVC logotype

Annotation of /projs/dtats/trunk/shared_source/c_tcl_base_7_5_w_mods/tclnotify.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25