Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclNotify.c
1810 views
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
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
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
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
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
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
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
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
585