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