/[dtapublic]/projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclnotify.c
ViewVC logotype

Annotation of /projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclnotify.c

Parent Directory Parent Directory | Revision Log Revision Log


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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25