001: /*
002: * Notifier.java --
003: *
004: * Implements the Jacl version of the Notifier class.
005: *
006: * Copyright (c) 1997 Sun Microsystems, Inc.
007: *
008: * See the file "license.terms" for information on usage and
009: * redistribution of this file, and for a DISCLAIMER OF ALL
010: * WARRANTIES.
011: *
012: * RCS: @(#) $Id: Notifier.java,v 1.11 2006/04/27 02:16:13 mdejong Exp $
013: *
014: */
015:
016: package tcl.lang;
017:
018: import java.util.HashMap;
019: import java.util.ArrayList;
020:
021: // Implements the Jacl version of the Notifier class. The Notifier is
022: // the lowest-level part of the event system. It is used by
023: // higher-level event sources such as file, JavaBean and timer
024: // events. The Notifier manages an event queue that holds TclEvent
025: // objects.
026: //
027: // The Jacl notifier is designed to run in a multi-threaded
028: // environment. Each notifier instance is associated with a primary
029: // thread. Any thread can queue (or dequeue) events using the
030: // queueEvent (or deleteEvents) call. However, only the primary thread
031: // may process events in the queue using the doOneEvent()
032: // call. Attepmts to call doOneEvent from a non-primary thread will
033: // cause a TclRuntimeError.
034: //
035: // This class does not have a public constructor and thus cannot be
036: // instantiated. The only way to for a Tcl extension to get an
037: // Notifier is to call Interp.getNotifier() (or
038: // Notifier.getNotifierForThread() ), which returns the Notifier for that
039: // interpreter (thread).
040:
041: public class Notifier implements EventDeleter {
042:
043: // First pending event, or null if none.
044:
045: private TclEvent firstEvent;
046:
047: // Last pending event, or null if none.
048:
049: private TclEvent lastEvent;
050:
051: // Last high-priority event in queue, or null if none.
052:
053: private TclEvent markerEvent;
054:
055: // Event that was just processed by serviceEvent
056:
057: private TclEvent servicedEvent = null;
058:
059: // The primary thread of this notifier. Only this thread should process
060: // events from the event queue.
061:
062: Thread primaryThread;
063:
064: // Stores the Notifier for each thread.
065:
066: private static HashMap notifierTable = new HashMap();
067:
068: // List of registered timer handlers.
069:
070: ArrayList timerList;
071:
072: // Used to distinguish older timer handlers from recently-created ones.
073:
074: int timerGeneration;
075:
076: // True if there is a pending timer event in the event queue, false
077: // otherwise.
078:
079: boolean timerPending;
080:
081: // List of registered idle handlers.
082:
083: ArrayList idleList;
084:
085: // Used to distinguish older idle handlers from recently-created ones.
086:
087: int idleGeneration;
088:
089: // Reference count of the notifier. It's used to tell when a notifier
090: // is no longer needed.
091:
092: int refCount;
093:
094: /*
095: *----------------------------------------------------------------------
096: *
097: * Notifier --
098: *
099: * Creates a Notifier instance.
100: *
101: * Side effects:
102: * Member fields are initialized.
103: *
104: *----------------------------------------------------------------------
105: */
106:
107: private Notifier(Thread primaryThread) // The primary thread for this Notifier.
108: {
109: if (primaryThread == null) {
110: throw new NullPointerException("primaryThread");
111: }
112: this .primaryThread = primaryThread;
113: firstEvent = null;
114: lastEvent = null;
115: markerEvent = null;
116:
117: timerList = new ArrayList();
118: timerGeneration = 0;
119: idleList = new ArrayList();
120: idleGeneration = 0;
121: timerPending = false;
122: refCount = 0;
123: }
124:
125: /*
126: *----------------------------------------------------------------------
127: *
128: * getNotifierForThread --
129: *
130: * Get the notifier for this thread, creating the Notifier,
131: * when necessary.
132: *
133: * Results:
134: * The Notifier for this thread.
135: *
136: * Side effects:
137: * The Notifier is created when necessary.
138: *
139: *----------------------------------------------------------------------
140: */
141:
142: public static synchronized Notifier getNotifierForThread(
143: Thread thread) // The thread that owns this Notifier.
144: {
145: Notifier notifier = (Notifier) notifierTable.get(thread);
146: if (notifier == null) {
147: notifier = new Notifier(thread);
148: notifierTable.put(thread, notifier);
149: }
150:
151: return notifier;
152: }
153:
154: /*
155: *----------------------------------------------------------------------
156: *
157: * preserve --
158: *
159: * Increment the reference count of the notifier. The notifier will
160: * be kept in the notifierTable (and alive) as long as its reference
161: * count is greater than zero.
162: *
163: * Results:
164: * None.
165: *
166: * Side effects:
167: * The refCount is incremented.
168: *
169: *----------------------------------------------------------------------
170: */
171:
172: public synchronized void preserve() {
173: if (refCount < 0) {
174: throw new TclRuntimeError(
175: "Attempting to preserve a freed Notifier");
176: }
177: ++refCount;
178: }
179:
180: /*
181: *----------------------------------------------------------------------
182: *
183: * release --
184: *
185: * Decrement the reference count of the notifier. The notifier will
186: * be freed when its refCount goes from one to zero.
187: *
188: * Results:
189: * None.
190: *
191: * Side effects:
192: * The notifier may be removed from the notifierTable when its
193: * refCount reaches zero.
194: *
195: *----------------------------------------------------------------------
196: */
197:
198: public synchronized void release() {
199: if ((refCount == 0) && (primaryThread != null)) {
200: throw new TclRuntimeError(
201: "Attempting to release a Notifier before it's preserved");
202: }
203: if (refCount <= 0) {
204: throw new TclRuntimeError(
205: "Attempting to release a freed Notifier");
206: }
207: --refCount;
208: if (refCount == 0) {
209: notifierTable.remove(primaryThread);
210: primaryThread = null;
211: }
212: }
213:
214: /*
215: *----------------------------------------------------------------------
216: *
217: * queueEvent --
218: *
219: * Insert an event into the event queue at one of three
220: * positions: the head, the tail, or before a floating marker.
221: * Events inserted before the marker will be processed in
222: * first-in-first-out order, but before any events inserted at
223: * the tail of the queue. Events inserted at the head of the
224: * queue will be processed in last-in-first-out order.
225: *
226: * Results:
227: * None.
228: *
229: * Side effects:
230: * If this method is invoked by a non-primary thread, the
231: * primaryThread of this Notifier will be notified about the new
232: * event.
233: *
234: *----------------------------------------------------------------------
235: */
236:
237: public synchronized void queueEvent(TclEvent evt, // The event to put in the queue.
238: int position) // One of TCL.QUEUE_TAIL,
239: // TCL.QUEUE_HEAD or TCL.QUEUE_MARK.
240: {
241: if (primaryThread == null) {
242: // queueEvent() invoked after the Notifier has been
243: // released. This could happen if this method was
244: // invoked after all the Interp objects in this
245: // thread have been disposed.
246:
247: throw new TclRuntimeError("Notifier.queueEvent() with "
248: + "no Interp() objects in the current thread");
249: }
250:
251: evt.notifier = this ;
252:
253: if (position == TCL.QUEUE_TAIL) {
254: // Append the event on the end of the queue.
255:
256: evt.next = null;
257:
258: if (firstEvent == null) {
259: firstEvent = evt;
260: } else {
261: lastEvent.next = evt;
262: }
263: lastEvent = evt;
264: } else if (position == TCL.QUEUE_HEAD) {
265: // Push the event on the head of the queue.
266:
267: evt.next = firstEvent;
268: if (firstEvent == null) {
269: lastEvent = evt;
270: }
271: firstEvent = evt;
272: } else if (position == TCL.QUEUE_MARK) {
273: // Insert the event after the current marker event and advance
274: // the marker to the new event.
275:
276: if (markerEvent == null) {
277: evt.next = firstEvent;
278: firstEvent = evt;
279: } else {
280: evt.next = markerEvent.next;
281: markerEvent.next = evt;
282: }
283: markerEvent = evt;
284: if (evt.next == null) {
285: lastEvent = evt;
286: }
287: } else {
288: // Wrong flag.
289:
290: throw new TclRuntimeError(
291: "wrong position \""
292: + position
293: + "\", must be TCL.QUEUE_HEAD, TCL.QUEUE_TAIL or TCL.QUEUE_MARK");
294: }
295:
296: if (Thread.currentThread() != primaryThread) {
297: notifyAll();
298: }
299: }
300:
301: /*
302: *----------------------------------------------------------------------
303: *
304: * deleteEvents --
305: *
306: * Calls an EventDeleter for each event in the queue and deletes
307: * those for which deleter.deleteEvent() returns 1. Events
308: * for which the deleter returns 0 are left in the queue. This
309: * method includes code to handle the special case of the
310: * Notifier wanting to delete a single event after is has
311: * been serviced. This method is concurrent safe.
312: *
313: * Results:
314: * None.
315: *
316: * Side effects:
317: * Potentially removes one or more events from the event queue.
318: *
319: *----------------------------------------------------------------------
320: */
321:
322: public synchronized void deleteEvents(EventDeleter deleter) // The deleter that checks whether an event
323: // should be removed.
324: {
325: TclEvent evt, prev;
326: TclEvent servicedEvent = null;
327:
328: // Handle the special case of deletion of a single event that was just
329: // processed by the serviceEvent() method.
330:
331: if (deleter == this ) {
332: servicedEvent = this .servicedEvent;
333: if (servicedEvent == null)
334: throw new TclRuntimeError(
335: "servicedEvent was not set by serviceEvent()");
336: this .servicedEvent = null;
337: }
338:
339: for (prev = null, evt = firstEvent; evt != null; evt = evt.next) {
340: if (((servicedEvent == null) && (deleter.deleteEvent(evt) == 1))
341: || (evt == servicedEvent)) {
342: if (evt == firstEvent) {
343: firstEvent = evt.next;
344: } else {
345: prev.next = evt.next;
346: }
347: if (evt.next == null) {
348: lastEvent = prev;
349: }
350: if (evt == markerEvent) {
351: markerEvent = prev;
352: }
353: if (evt == servicedEvent) {
354: servicedEvent = null;
355: break; // Just service this one event in the special case
356: }
357: } else {
358: prev = evt;
359: }
360: }
361: if (servicedEvent != null) {
362: throw new TclRuntimeError(
363: "servicedEvent was not removed from the queue");
364: }
365: }
366:
367: /*
368: *----------------------------------------------------------------------
369: *
370: * deleteEvent --
371: *
372: * This method is required to implement the EventDeleter interface
373: * It is not actually used though, see deleteEvents method for
374: * special casing of the deletion of a specific event.
375: *
376: * Results:
377: * None.
378: *
379: * Side effects:
380: * None.
381: *
382: *----------------------------------------------------------------------
383: */
384:
385: public int deleteEvent(TclEvent evt) {
386: throw new TclRuntimeError(
387: "The Notifier.deleteEvent() method should not be called");
388: }
389:
390: /*
391: *----------------------------------------------------------------------
392: *
393: * serviceEvent --
394: *
395: * Process one event from the event queue.
396: *
397: * Results:
398: * The return value is 1 if the procedure actually found an event
399: * to process. If no processing occurred, then 0 is returned.
400: *
401: * Side effects:
402: * Invokes all of the event handlers for the highest priority
403: * event in the event queue. May collapse some events into a
404: * single event or discard stale events.
405: *
406: *----------------------------------------------------------------------
407: */
408:
409: int serviceEvent(int flags) // Indicates what events should be processed.
410: // May be any combination of TCL.WINDOW_EVENTS
411: // TCL.FILE_EVENTS, TCL.TIMER_EVENTS, or other
412: // flags defined elsewhere. Events not
413: // matching this will be skipped for processing
414: // later.
415: {
416: TclEvent evt;
417:
418: // No event flags is equivalent to TCL_ALL_EVENTS.
419:
420: if ((flags & TCL.ALL_EVENTS) == 0) {
421: flags |= TCL.ALL_EVENTS;
422: }
423:
424: // Loop through all the events in the queue until we find one
425: // that can actually be handled.
426:
427: evt = null;
428: while ((evt = getAvailableEvent(evt)) != null) {
429: // Call the handler for the event. If it actually handles the
430: // event then free the storage for the event. There are two
431: // tricky things here, both stemming from the fact that the event
432: // code may be re-entered while servicing the event:
433: //
434: // 1. Set the "isProcessing" field to true. This is a signal to
435: // ourselves that we shouldn't reexecute the handler if the
436: // event loop is re-entered.
437: // 2. When freeing the event, must search the queue again from the
438: // front to find it. This is because the event queue could
439: // change almost arbitrarily while handling the event, so we
440: // can't depend on pointers found now still being valid when
441: // the handler returns.
442:
443: evt.isProcessing = true;
444:
445: if (evt.processEvent(flags) != 0) {
446: evt.isProcessed = true;
447: // Don't allocate/grab the monitor for the event unless sync()
448: // has been called in another thread. This is thread safe
449: // since sync() checks the isProcessed flag before calling wait.
450: if (evt.needsNotify) {
451: synchronized (evt) {
452: evt.notifyAll();
453: }
454: }
455: // Remove this specific event from the queue
456: servicedEvent = evt;
457: deleteEvents(this );
458: return 1;
459: } else {
460: // The event wasn't actually handled, so we have to
461: // restore the isProcessing field to allow the event to be
462: // attempted again.
463:
464: evt.isProcessing = false;
465: }
466:
467: // The handler for this event asked to defer it. Just go on to
468: // the next event.
469:
470: continue;
471: }
472: return 0;
473: }
474:
475: /*
476: *----------------------------------------------------------------------
477: *
478: * getAvailableEvent --
479: *
480: * Search through the internal event list to find the first event
481: * that is has not being processed AND the event is not equal to the given
482: * 'skipEvent'. This method is concurrent safe.
483: *
484: * Results:
485: * The return value is a pointer to the first found event that can be
486: * processed. If no event is found, this method returns null.
487: *
488: * Side effects:
489: * This method synchronizes on the 'notifierMutex', which will block any
490: * other thread from adding or removing events from the event queue.
491: *
492: *----------------------------------------------------------------------
493: */
494:
495: private synchronized TclEvent getAvailableEvent(TclEvent skipEvent) // Indicates that the given event should not
496: // be returned. This argument can be null.
497: {
498: TclEvent evt;
499:
500: for (evt = firstEvent; evt != null; evt = evt.next) {
501: if ((evt.isProcessing == false)
502: && (evt.isProcessed == false) && (evt != skipEvent)) {
503: return evt;
504: }
505: }
506: return null;
507: }
508:
509: /*
510: *----------------------------------------------------------------------
511: *
512: * doOneEvent --
513: *
514: * Process a single event of some sort. If there's no work to
515: * do, wait for an event to occur, then process it. May delay
516: * execution of process while waiting for an event, unless
517: * TCL.DONT_WAIT is set in the flags argument.
518: *
519: * Results:
520: * The return value is 1 if the procedure actually found an event
521: * to process. If no processing occurred, then 0 is returned
522: * (this can happen if the TCL.DONT_WAIT flag is set or if there
523: * are no event handlers to wait for in the set specified by
524: * flags).
525: *
526: * Side effects:
527: * May delay execution of process while waiting for an event,
528: * unless TCL.DONT_WAIT is set in the flags argument. Event
529: * sources are invoked to check for and queue events. Event
530: * handlers may produce arbitrary side effects.
531: *
532: *----------------------------------------------------------------------
533: */
534:
535: public int doOneEvent(int flags) // Miscellaneous flag values: may be any
536: // combination of TCL.DONT_WAIT,
537: // TCL.WINDOW_EVENTS, TCL.FILE_EVENTS,
538: // TCL.TIMER_EVENTS, TCL.IDLE_EVENTS,
539: // or others defined by event sources.
540: {
541: final boolean debug = false;
542:
543: int result = 0;
544:
545: if (primaryThread == null) {
546: // queueEvent() invoked after the Notifier has been
547: // released. This could happen if this method was
548: // invoked after all the Interp objects in this
549: // thread have been disposed.
550:
551: throw new TclRuntimeError("Notifier.doOneEvent() with "
552: + "no Interp() objects in the current thread");
553: }
554:
555: // No event flags is equivalent to TCL_ALL_EVENTS.
556:
557: if ((flags & TCL.ALL_EVENTS) == 0) {
558: flags |= TCL.ALL_EVENTS;
559: }
560:
561: // The core of this procedure is an infinite loop, even though
562: // we only service one event. The reason for this is that we
563: // may be processing events that don't do anything inside of Tcl.
564:
565: while (true) {
566: // If idle events are the only things to service, skip the
567: // main part of the loop and go directly to handle idle
568: // events (i.e. don't wait even if TCL_DONT_WAIT isn't set).
569:
570: if ((flags & TCL.ALL_EVENTS) == TCL.IDLE_EVENTS) {
571: return serviceIdle();
572: }
573:
574: long sysTime = System.currentTimeMillis();
575:
576: // If some timers have been expired, queue them into the
577: // event queue. We can't process expired times right away,
578: // because there may already be other events on the queue.
579:
580: if (!timerPending && (timerList.size() > 0)) {
581: TimerHandler h = (TimerHandler) timerList.get(0);
582:
583: if (h.atTime <= sysTime) {
584: TimerEvent event = new TimerEvent();
585: event.notifier = this ;
586: queueEvent(event, TCL.QUEUE_TAIL);
587: timerPending = true;
588: }
589: }
590:
591: // Service a queued event, if there are any.
592:
593: if (serviceEvent(flags) != 0) {
594: result = 1;
595: break;
596: }
597:
598: // There is no event on the queue. Check for idle events.
599:
600: if ((flags & TCL.IDLE_EVENTS) != 0) {
601: if (serviceIdle() != 0) {
602: result = 1;
603: break;
604: }
605: }
606:
607: if ((flags & TCL.DONT_WAIT) != 0) {
608: break;
609: }
610:
611: // We don't have any event to service. We'll wait if
612: // TCL.DONT_WAIT. When the following wait() call returns,
613: // one of the following things may happen:
614: //
615: // (1) waitTime milliseconds has elasped (if waitTime != 0);
616: //
617: // (2) The primary notifier has been notify()'ed by other threads:
618: // (a) an event is queued by queueEvent().
619: // (b) a timer handler was created by new TimerHandler();
620: // (c) an idle handler was created by new IdleHandler();
621: // (3) We receive an InterruptedException.
622: //
623:
624: try {
625: // Don't acquire the monitor until we are about to wait
626: // for notification from another thread. It is critical
627: // that this entire method not be synchronized since
628: // a call to processEvent via serviceEvent could take
629: // a very long time. We don't want the monitor held
630: // during that time since that would force calls to
631: // queueEvent in other threads to wait.
632:
633: synchronized (this ) {
634: if (timerList.size() > 0) {
635: TimerHandler h = (TimerHandler) timerList
636: .get(0);
637: long waitTime = h.atTime - sysTime;
638: if (waitTime > 0) {
639: wait(waitTime);
640: }
641: } else {
642: wait();
643: }
644: } // synchronized (this)
645: } catch (InterruptedException e) {
646: // We ignore any InterruptedException and loop continuously
647: // until we receive an event.
648: }
649: }
650:
651: return result;
652: }
653:
654: /*
655: *----------------------------------------------------------------------
656: *
657: * serviceIdle --
658: *
659: * Service all idle handlers that have been registered in the
660: * notifier.
661: *
662: * Results:
663: * 1 if any idle handlers have been processed. 0 otherwise.
664: *
665: * Side effects:
666: * The idle handlers may have arbitrary side effects.
667: *
668: *----------------------------------------------------------------------
669: */
670:
671: private int serviceIdle() {
672: int result = 0;
673: int gen = idleGeneration;
674: idleGeneration++;
675:
676: // The code below is trickier than it may look, for the following
677: // reasons:
678: //
679: // 1. New handlers can get added to the list while the current
680: // one is being processed. If new ones get added, we don't
681: // want to process them during this pass through the list (want
682: // to check for other work to do first). This is implemented
683: // using the generation number in the handler: new handlers
684: // will have a different generation than any of the ones currently
685: // on the list.
686: // 2. The handler can call doOneEvent, so we have to remove
687: // the handler from the list before calling it. Otherwise an
688: // infinite loop could result.
689:
690: while (idleList.size() > 0) {
691: IdleHandler h = (IdleHandler) idleList.get(0);
692: if (h.generation > gen) {
693: break;
694: }
695: idleList.remove(0);
696: if (h.invoke() != 0) {
697: result = 1;
698: }
699: }
700:
701: return result;
702: }
703:
704: /*
705: *----------------------------------------------------------------------
706: *
707: * hasActiveInterps --
708: *
709: * Return true if this Notifier is processing events for 1
710: * or more active Interp objects. When an Interp is disposed()
711: * of it decrements the refCount of the Notifier object.
712: * When the last active Interp in a thread is disposed of,
713: * the Notifier is disposed of. This method will return true
714: * for a Notifier object that has active Interp object and
715: * false when the Notifier has been disposed of because the
716: * last active interp was disposed of.
717: *
718: * Results:
719: * None.
720: *
721: * Side effects:
722: * None.
723: *
724: *----------------------------------------------------------------------
725: */
726:
727: public synchronized boolean hasActiveInterps() {
728: if (primaryThread == null) {
729: return false;
730: } else {
731: return true;
732: }
733: }
734:
735: /*
736: *----------------------------------------------------------------------
737: *
738: * processTclEvents --
739: *
740: * This util method is provided for use in a thread
741: * dedicated to processing events from the Tcl event queue.
742: * This method must only be used as the outermost event
743: * processing loop. It is not legal to use this method
744: * from any code that could be invoked by Tcl. This
745: * method supports interps that will make use of the
746: * setInterrupted() API.
747: *
748: * If only one Interp exists in the thread and the
749: * interp is interrupted, then this method will return.
750: * If more than one interp exists in the thread and
751: * one interp is interrupted, then events for the other
752: * interps will continue to be processed. This method
753: * will return when all the interps in the current
754: * thread have been disposed of. This is a convience
755: * method only, there is no reason this logic could
756: * not appear in user code.
757: *
758: *----------------------------------------------------------------------
759: */
760:
761: public static void processTclEvents(Notifier notifier) {
762: while (notifier.hasActiveInterps()) {
763: try {
764: notifier.doOneEvent(TCL.ALL_EVENTS);
765: } catch (TclInterruptedException tie) {
766: tie.disposeInterruptedInterp();
767: }
768: }
769:
770: // The while loop will exit when the last
771: // interp is disposed of. If this was
772: // called by Thread.run() then the thread
773: // will die when that method terminates.
774: // If "exit" is called the process will
775: // terminate without unwinding the stack.
776:
777: return;
778: }
779:
780: } // end Notifier
781:
782: // This class is used to service timer events. When one or more timers
783: // have expired but not processed, one TimerEvent will be generated
784: // and put into the event queue. When the TimerEvent is pulled off the
785: // queue, it will process all expired timers in a bunch.
786:
787: class TimerEvent extends TclEvent {
788:
789: // The notifier that owns this TimerEvent.
790:
791: Notifier notifier;
792:
793: /*
794: *----------------------------------------------------------------------
795: *
796: * TimerHandlerEventProc -> processEvent
797: *
798: * This function is called by Tcl_ServiceEvent when a timer event reaches
799: * the front of the event queue. This function handles the event by
800: * invoking the callbacks for all timers that are ready.
801: *
802: * Results:
803: * Returns 1 if the event was handled, meaning it should be removed from
804: * the queue. Returns 0 if the event was not handled, meaning it should
805: * stay on the queue. The only time the event isn't handled is if the
806: * TCL.TIMER_EVENTS flag bit isn't set.
807: *
808: * Side effects:
809: * The TimerHandler may have arbitrary side effects while
810: * processing the event.
811: *
812: *----------------------------------------------------------------------
813: */
814:
815: public int processEvent(int flags) // Same as flags passed to Notifier.doOneEvent.
816: {
817: // Do nothing if timers aren't enabled. This leaves the event on the
818: // queue, so we will get to it as soon as ServiceEvents() is called with
819: // timers enabled.
820:
821: if ((flags & TCL.TIMER_EVENTS) == 0) {
822: return 0;
823: }
824:
825: long sysTime = System.currentTimeMillis();
826: int gen = notifier.timerGeneration;
827: notifier.timerGeneration++;
828:
829: // The code below is trickier than it may look, for the following
830: // reasons:
831: //
832: // 1. New handlers can get added to the list while the current
833: // one is being processed. If new ones get added, we don't
834: // want to process them during this pass through the list to
835: // avoid starving other event sources. This is implemented
836: // using the timer generation number: new handlers will have
837: // a newer generation number than any of the ones currently on
838: // the list.
839: // 2. The handler can call doOneEvent, so we have to remove
840: // the handler from the list before calling it. Otherwise an
841: // infinite loop could result.
842: // 3. Tcl_DeleteTimerHandler can be called to remove an element from the
843: // list while a handler is executing, so the list could change
844: // structure during the call.
845: // 4. Because we only fetch the current time before entering the loop,
846: // the only way a new timer will even be considered runnable is if
847: // its expiration time is within the same millisecond as the
848: // current time. This is fairly likely on Windows, since it has
849: // a course granularity clock. Since timers are placed
850: // on the queue in time order with the most recently created
851: // handler appearing after earlier ones with the same expiration
852: // time, we don't have to worry about newer generation timers
853: // appearing before later ones.
854:
855: notifier.timerPending = false;
856:
857: while (notifier.timerList.size() > 0) {
858: TimerHandler h = (TimerHandler) notifier.timerList.get(0);
859: if (h.generation > gen) {
860: break;
861: }
862: if (h.atTime > sysTime) {
863: break;
864: }
865: notifier.timerList.remove(0);
866: h.invoke();
867: }
868:
869: return 1;
870: }
871:
872: } // end TimerEvent
|