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 */ |