1 /*
2  * tclNotify.c --
3  *
4  *	This file provides the parts of the Tcl event notifier that are
5  *	the same on all platforms, plus a few other parts that are used
6  *	on more than one platform but not all.
7  *
8  *	The notifier is the lowest-level part of the event system.  It
9  *	manages an event queue that holds Tcl_Event structures and a list
10  *	of event sources that can add events to the queue.  It also
11  *	contains the procedure Tcl_DoOneEvent that invokes the event
12  *	sources and blocks to wait for new events, but Tcl_DoOneEvent
13  *	is in the platform-specific part of the notifier (in files like
14  *	tclUnixNotify.c).
15  *
16  * Copyright (c) 1995 Sun Microsystems, Inc.
17  *
18  * See the file "license.terms" for information on usage and redistribution
19  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
20  *
21  * SCCS: @(#) tclNotify.c 1.7 96/09/19 16:40:16
22  */
23 
24 #include "tclInt.h"
25 #include "tclPort.h"
26 
27 /*
28  * The following variable records the address of the first event
29  * source in the list of all event sources for the application.
30  * This variable is accessed by the notifier to traverse the list
31  * and invoke each event source.
32  */
33 
34 TclEventSource *tclFirstEventSourcePtr = NULL;
35 
36 /*
37  * The following variables indicate how long to block in the event
38  * notifier the next time it blocks (default:  block forever).
39  */
40 
41 static int blockTimeSet = 0;	/* 0 means there is no maximum block
42 				 * time:  block forever. */
43 static Tcl_Time blockTime;	/* If blockTimeSet is 1, gives the
44 				 * maximum elapsed time for the next block. */
45 
46 /*
47  * The following variables keep track of the event queue.  In addition
48  * to the first (next to be serviced) and last events in the queue,
49  * we keep track of a "marker" event.  This provides a simple priority
50  * mechanism whereby events can be inserted at the front of the queue
51  * but behind all other high-priority events already in the queue (this
52  * is used for things like a sequence of Enter and Leave events generated
53  * during a grab in Tk).
54  */
55 
56 static Tcl_Event *firstEventPtr = NULL;
57 				/* First pending event, or NULL if none. */
58 static Tcl_Event *lastEventPtr = NULL;
59 				/* Last pending event, or NULL if none. */
60 static Tcl_Event *markerEventPtr = NULL;
61 				/* Last high-priority event in queue, or
62 				 * NULL if none. */
63 
64 /*
65  * Prototypes for procedures used only in this file:
66  */
67 
68 static int		ServiceEvent _ANSI_ARGS_((int flags));
69 
70 /*
71  *----------------------------------------------------------------------
72  *
73  * Tcl_CreateEventSource --
74  *
75  *	This procedure is invoked to create a new source of events.
76  *	The source is identified by a procedure that gets invoked
77  *	during Tcl_DoOneEvent to check for events on that source
78  *	and queue them.
79  *
80  *
81  * Results:
82  *	None.
83  *
84  * Side effects:
85  *	SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
86  *	runs out of things to do.  SetupProc will be invoked before
87  *	Tcl_DoOneEvent calls select or whatever else it uses to wait
88  *	for events.  SetupProc typically calls functions like Tcl_WatchFile
89  *	or Tcl_SetMaxBlockTime to indicate what to wait for.
90  *
91  *	CheckProc is called after select or whatever operation was actually
92  *	used to wait.  It figures out whether anything interesting actually
93  *	happened (e.g. by calling Tcl_FileReady), and then calls
94  *	Tcl_QueueEvent to queue any events that are ready.
95  *
96  *	Each of these procedures is passed two arguments, e.g.
97  *		(*checkProc)(ClientData clientData, int flags));
98  *	ClientData is the same as the clientData argument here, and flags
99  *	is a combination of things like TCL_FILE_EVENTS that indicates
100  *	what events are of interest:  setupProc and checkProc use flags
101  *	to figure out whether their events are relevant or not.
102  *
103  *----------------------------------------------------------------------
104  */
105 
106 void
Tcl_CreateEventSource(setupProc,checkProc,clientData)107 Tcl_CreateEventSource(setupProc, checkProc, clientData)
108     Tcl_EventSetupProc *setupProc;	/* Procedure to invoke to figure out
109 					 * what to wait for. */
110     Tcl_EventCheckProc *checkProc;	/* Procedure to call after waiting
111 					 * to see what happened. */
112     ClientData clientData;		/* One-word argument to pass to
113 					 * setupProc and checkProc. */
114 {
115     TclEventSource *sourcePtr;
116 
117     sourcePtr = (TclEventSource *) ckalloc(sizeof(TclEventSource));
118     sourcePtr->setupProc = setupProc;
119     sourcePtr->checkProc = checkProc;
120     sourcePtr->clientData = clientData;
121     sourcePtr->nextPtr = tclFirstEventSourcePtr;
122     tclFirstEventSourcePtr = sourcePtr;
123 }
124 
125 /*
126  *----------------------------------------------------------------------
127  *
128  * Tcl_DeleteEventSource --
129  *
130  *	This procedure is invoked to delete the source of events
131  *	given by proc and clientData.
132  *
133  * Results:
134  *	None.
135  *
136  * Side effects:
137  *	The given event source is cancelled, so its procedure will
138  *	never again be called.  If no such source exists, nothing
139  *	happens.
140  *
141  *----------------------------------------------------------------------
142  */
143 
144 void
Tcl_DeleteEventSource(setupProc,checkProc,clientData)145 Tcl_DeleteEventSource(setupProc, checkProc, clientData)
146     Tcl_EventSetupProc *setupProc;	/* Procedure to invoke to figure out
147 					 * what to wait for. */
148     Tcl_EventCheckProc *checkProc;	/* Procedure to call after waiting
149 					 * to see what happened. */
150     ClientData clientData;		/* One-word argument to pass to
151 					 * setupProc and checkProc. */
152 {
153     TclEventSource *sourcePtr, *prevPtr;
154 
155     for (sourcePtr = tclFirstEventSourcePtr, prevPtr = NULL;
156 	    sourcePtr != NULL;
157 	    prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
158 	if ((sourcePtr->setupProc != setupProc)
159 		|| (sourcePtr->checkProc != checkProc)
160 		|| (sourcePtr->clientData != clientData)) {
161 	    continue;
162 	}
163 	if (prevPtr == NULL) {
164 	    tclFirstEventSourcePtr = sourcePtr->nextPtr;
165 	} else {
166 	    prevPtr->nextPtr = sourcePtr->nextPtr;
167 	}
168 	ckfree((char *) sourcePtr);
169 	return;
170     }
171 }
172 
173 /*
174  *----------------------------------------------------------------------
175  *
176  * Tcl_QueueEvent --
177  *
178  *	Insert an event into the Tk event queue at one of three
179  *	positions: the head, the tail, or before a floating marker.
180  *	Events inserted before the marker will be processed in
181  *	first-in-first-out order, but before any events inserted at
182  *	the tail of the queue.  Events inserted at the head of the
183  *	queue will be processed in last-in-first-out order.
184  *
185  * Results:
186  *	None.
187  *
188  * Side effects:
189  *	None.
190  *
191  *----------------------------------------------------------------------
192  */
193 
194 void
Tcl_QueueEvent(evPtr,position)195 Tcl_QueueEvent(evPtr, position)
196     Tcl_Event* evPtr;		/* Event to add to queue.  The storage
197 				 * space must have been allocated the caller
198 				 * with malloc (ckalloc), and it becomes
199 				 * the property of the event queue.  It
200 				 * will be freed after the event has been
201 				 * handled. */
202     Tcl_QueuePosition position;	/* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
203 				 * TCL_QUEUE_MARK. */
204 {
205     if (position == TCL_QUEUE_TAIL) {
206 	/*
207 	 * Append the event on the end of the queue.
208 	 */
209 
210 	evPtr->nextPtr = NULL;
211 	if (firstEventPtr == NULL) {
212 	    firstEventPtr = evPtr;
213 	} else {
214 	    lastEventPtr->nextPtr = evPtr;
215 	}
216 	lastEventPtr = evPtr;
217     } else if (position == TCL_QUEUE_HEAD) {
218 	/*
219 	 * Push the event on the head of the queue.
220 	 */
221 
222 	evPtr->nextPtr = firstEventPtr;
223 	if (firstEventPtr == NULL) {
224 	    lastEventPtr = evPtr;
225 	}
226 	firstEventPtr = evPtr;
227     } else if (position == TCL_QUEUE_MARK) {
228 	/*
229 	 * Insert the event after the current marker event and advance
230 	 * the marker to the new event.
231 	 */
232 
233 	if (markerEventPtr == NULL) {
234 	    evPtr->nextPtr = firstEventPtr;
235 	    firstEventPtr = evPtr;
236 	} else {
237 	    evPtr->nextPtr = markerEventPtr->nextPtr;
238 	    markerEventPtr->nextPtr = evPtr;
239 	}
240 	markerEventPtr = evPtr;
241 	if (evPtr->nextPtr == NULL) {
242 	    lastEventPtr = evPtr;
243 	}
244     }
245 }
246 
247 /*
248  *----------------------------------------------------------------------
249  *
250  * Tcl_DeleteEvents --
251  *
252  *	Calls a procedure for each event in the queue and deletes those
253  *	for which the procedure returns 1. Events for which the
254  *	procedure returns 0 are left in the queue.
255  *
256  * Results:
257  *	None.
258  *
259  * Side effects:
260  *	Potentially removes one or more events from the event queue.
261  *
262  *----------------------------------------------------------------------
263  */
264 
265 void
Tcl_DeleteEvents(proc,clientData)266 Tcl_DeleteEvents(proc, clientData)
267     Tcl_EventDeleteProc *proc;		/* The procedure to call. */
268     ClientData clientData;    		/* type-specific data. */
269 {
270     Tcl_Event *evPtr, *prevPtr, *hold;
271 
272     for (prevPtr = (Tcl_Event *) NULL, evPtr = firstEventPtr;
273              evPtr != (Tcl_Event *) NULL;
274              ) {
275         if ((*proc) (evPtr, clientData) == 1) {
276             if (firstEventPtr == evPtr) {
277                 firstEventPtr = evPtr->nextPtr;
278                 if (evPtr->nextPtr == (Tcl_Event *) NULL) {
279                     lastEventPtr = (Tcl_Event *) NULL;
280                 }
281             } else {
282                 prevPtr->nextPtr = evPtr->nextPtr;
283             }
284             hold = evPtr;
285             evPtr = evPtr->nextPtr;
286             ckfree((char *) hold);
287         } else {
288             prevPtr = evPtr;
289             evPtr = evPtr->nextPtr;
290         }
291     }
292 }
293 
294 /*
295  *----------------------------------------------------------------------
296  *
297  * ServiceEvent --
298  *
299  *	Process one event from the event queue.  This routine is called
300  *	by the notifier whenever it wants Tk to process an event.
301  *
302  * Results:
303  *	The return value is 1 if the procedure actually found an event
304  *	to process.  If no processing occurred, then 0 is returned.
305  *
306  * Side effects:
307  *	Invokes all of the event handlers for the highest priority
308  *	event in the event queue.  May collapse some events into a
309  *	single event or discard stale events.
310  *
311  *----------------------------------------------------------------------
312  */
313 
314 static int
ServiceEvent(flags)315 ServiceEvent(flags)
316     int flags;			/* Indicates what events should be processed.
317 				 * May be any combination of TCL_WINDOW_EVENTS
318 				 * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
319 				 * flags defined elsewhere.  Events not
320 				 * matching this will be skipped for processing
321 				 * later. */
322 {
323     Tcl_Event *evPtr, *prevPtr;
324     Tcl_EventProc *proc;
325 
326     /*
327      * No event flags is equivalent to TCL_ALL_EVENTS.
328      */
329 
330     if ((flags & TCL_ALL_EVENTS) == 0) {
331 	flags |= TCL_ALL_EVENTS;
332     }
333 
334     /*
335      * Loop through all the events in the queue until we find one
336      * that can actually be handled.
337      */
338 
339     for (evPtr = firstEventPtr; evPtr != NULL; evPtr = evPtr->nextPtr) {
340 	/*
341 	 * Call the handler for the event.  If it actually handles the
342 	 * event then free the storage for the event.  There are two
343 	 * tricky things here, but stemming from the fact that the event
344 	 * code may be re-entered while servicing the event:
345 	 *
346 	 * 1. Set the "proc" field to NULL.  This is a signal to ourselves
347 	 *    that we shouldn't reexecute the handler if the event loop
348 	 *    is re-entered.
349 	 * 2. When freeing the event, must search the queue again from the
350 	 *    front to find it.  This is because the event queue could
351 	 *    change almost arbitrarily while handling the event, so we
352 	 *    can't depend on pointers found now still being valid when
353 	 *    the handler returns.
354 	 */
355 
356 	proc = evPtr->proc;
357 	evPtr->proc = NULL;
358 	if ((proc != NULL) && (*proc)(evPtr, flags)) {
359 	    if (firstEventPtr == evPtr) {
360 		firstEventPtr = evPtr->nextPtr;
361 		if (evPtr->nextPtr == NULL) {
362 		    lastEventPtr = NULL;
363 		}
364 		if (markerEventPtr == evPtr) {
365 		    markerEventPtr = NULL;
366 		}
367 	    } else {
368 		for (prevPtr = firstEventPtr; prevPtr->nextPtr != evPtr;
369 			prevPtr = prevPtr->nextPtr) {
370 		    /* Empty loop body. */
371 		}
372 		prevPtr->nextPtr = evPtr->nextPtr;
373 		if (evPtr->nextPtr == NULL) {
374 		    lastEventPtr = prevPtr;
375 		}
376 		if (markerEventPtr == evPtr) {
377 		    markerEventPtr = prevPtr;
378 		}
379 	    }
380 	    ckfree((char *) evPtr);
381 	    return 1;
382 	} else {
383 	    /*
384 	     * The event wasn't actually handled, so we have to restore
385 	     * the proc field to allow the event to be attempted again.
386 	     */
387 
388 	    evPtr->proc = proc;
389 	}
390 
391 	/*
392 	 * The handler for this event asked to defer it.  Just go on to
393 	 * the next event.
394 	 */
395 
396 	continue;
397     }
398     return 0;
399 }
400 
401 /*
402  *----------------------------------------------------------------------
403  *
404  * Tcl_SetMaxBlockTime --
405  *
406  *	This procedure is invoked by event sources to tell the notifier
407  *	how long it may block the next time it blocks.  The timePtr
408  *	argument gives a maximum time;  the actual time may be less if
409  *	some other event source requested a smaller time.
410  *
411  * Results:
412  *	None.
413  *
414  * Side effects:
415  *	May reduce the length of the next sleep in the notifier.
416  *
417  *----------------------------------------------------------------------
418  */
419 
420 void
Tcl_SetMaxBlockTime(timePtr)421 Tcl_SetMaxBlockTime(timePtr)
422     Tcl_Time *timePtr;		/* Specifies a maximum elapsed time for
423 				 * the next blocking operation in the
424 				 * event notifier. */
425 {
426     if (!blockTimeSet || (timePtr->sec < blockTime.sec)
427 	    || ((timePtr->sec == blockTime.sec)
428 	    && (timePtr->usec < blockTime.usec))) {
429 	blockTime = *timePtr;
430 	blockTimeSet = 1;
431     }
432 }
433 
434 /*
435  *----------------------------------------------------------------------
436  *
437  * Tcl_DoOneEvent --
438  *
439  *	Process a single event of some sort.  If there's no work to
440  *	do, wait for an event to occur, then process it.
441  *
442  * Results:
443  *	The return value is 1 if the procedure actually found an event
444  *	to process.  If no processing occurred, then 0 is returned (this
445  *	can happen if the TCL_DONT_WAIT flag is set or if there are no
446  *	event handlers to wait for in the set specified by flags).
447  *
448  * Side effects:
449  *	May delay execution of process while waiting for an event,
450  *	unless TCL_DONT_WAIT is set in the flags argument.  Event
451  *	sources are invoked to check for and queue events.  Event
452  *	handlers may produce arbitrary side effects.
453  *
454  *----------------------------------------------------------------------
455  */
456 
457 int
Tcl_DoOneEvent(flags)458 Tcl_DoOneEvent(flags)
459     int flags;			/* Miscellaneous flag values:  may be any
460 				 * combination of TCL_DONT_WAIT,
461 				 * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
462 				 * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
463 				 * others defined by event sources. */
464 {
465     TclEventSource *sourcePtr;
466     Tcl_Time *timePtr;
467 
468     /*
469      * No event flags is equivalent to TCL_ALL_EVENTS.
470      */
471 
472     if ((flags & TCL_ALL_EVENTS) == 0) {
473 	flags |= TCL_ALL_EVENTS;
474     }
475 
476     /*
477      * The core of this procedure is an infinite loop, even though
478      * we only service one event.  The reason for this is that we
479      * might think we have an event ready (e.g. the connection to
480      * the server becomes readable), but then we might discover that
481      * there's nothing interesting on that connection, so no event
482      * was serviced.  Or, the select operation could return prematurely
483      * due to a signal.  The easiest thing in both these cases is
484      * just to loop back and try again.
485      */
486 
487     while (1) {
488 
489 	sh_sigcheck(0); /* XXX: tksh specific */
490 
491 	/*
492 	 * The first thing we do is to service any asynchronous event
493 	 * handlers.
494 	 */
495 
496 	if (Tcl_AsyncReady()) {
497 	    (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
498 	    return 1;
499 	}
500 
501 	/*
502 	 * If idle events are the only things to service, skip the
503 	 * main part of the loop and go directly to handle idle
504 	 * events (i.e. don't wait even if TCL_DONT_WAIT isn't set.
505 	 */
506 
507 	if (flags == TCL_IDLE_EVENTS) {
508 	    flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
509 	    goto idleEvents;
510 	}
511 
512 	/*
513 	 * Ask Tk to service a queued event, if there are any.
514 	 */
515 
516 	if (ServiceEvent(flags)) {
517 	    return 1;
518 	}
519 
520 	/*
521 	 * There are no events already queued.  Invoke all of the
522 	 * event sources to give them a chance to setup for the wait.
523 	 */
524 
525 	blockTimeSet = 0;
526 	for (sourcePtr = tclFirstEventSourcePtr; sourcePtr != NULL;
527 		sourcePtr = sourcePtr->nextPtr) {
528 	    (*sourcePtr->setupProc)(sourcePtr->clientData, flags);
529 	}
530 	if ((flags & TCL_DONT_WAIT) ||
531 		((flags & TCL_IDLE_EVENTS) && TclIdlePending())) {
532 	    /*
533 	     * Don't block:  there are idle events waiting, or we don't
534 	     * care about idle events anyway, or the caller asked us not
535 	     * to block.
536 	     */
537 
538 	    blockTime.sec = 0;
539 	    blockTime.usec = 0;
540 	    timePtr = &blockTime;
541 	} else if (blockTimeSet) {
542 	    timePtr = &blockTime;
543 	} else {
544 	    timePtr = NULL;
545 	}
546 
547 	/*
548 	 * Wait until an event occurs or the timer expires.
549 	 */
550 
551 	if (Tcl_WaitForEvent(timePtr) == TCL_ERROR) {
552 	    return 0;
553 	}
554 
555 	/*
556 	 * Give each of the event sources a chance to queue events,
557 	 * then call ServiceEvent and give it another chance to
558 	 * service events.
559 	 */
560 
561 	for (sourcePtr = tclFirstEventSourcePtr; sourcePtr != NULL;
562 		sourcePtr = sourcePtr->nextPtr) {
563 	    (*sourcePtr->checkProc)(sourcePtr->clientData, flags);
564 	}
565 	if (ServiceEvent(flags)) {
566 	    return 1;
567 	}
568 
569 	/*
570 	 * We've tried everything at this point, but nobody had anything
571 	 * to do.  Check for idle events.  If none, either quit or go back
572 	 * to the top and try again.
573 	 */
574 
575 	idleEvents:
576 	if ((flags & TCL_IDLE_EVENTS) && TclServiceIdle()) {
577 	    return 1;
578 	}
579 	if (flags & TCL_DONT_WAIT) {
580 	    return 0;
581 	}
582     }
583 }
584