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

Contents of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclnotify.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (show annotations) (download)
Sat Nov 5 11:07:06 2016 UTC (8 years, 1 month ago) by dashley
File MIME type: text/plain
File size: 30034 byte(s)
Set EOL properties appropriately to facilitate simultaneous Linux and Windows development.
1 /* $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