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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations) (download)
Fri Oct 14 01:50:00 2016 UTC (7 years, 5 months ago) by dashley
File MIME type: text/plain
File size: 31446 byte(s)
Move shared source code to commonize.
1 /* $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