Contents

Swinging Duke
Feedback Button
Left ArrowRight Arrow

The Swing Event Queue

The EventQueue developed for Swing can provide a convenient means of synchronization of events from multiple sources. Currently, the AWT uses the EventQueue to deliver events from native code into Java-based components. Until now, however, access to the EventQueue has been a secure operation, and consequently was not always available to applet or library writers. Also, the API that provides information on how to work with the EventQueue has up to now been fairly limited. This specification documents several enhancements to the EventQueue that are planned for JDK 1.2. These enhancements are designed to make the EventQueue easier to work with, more accessible, and more versatile than it has been in the past.


Goals

Several component problems are solved quite simply with the kind of enhanced EventQueue that is being developed for Swing. They include:

Also, there are some application implementation problems that Swing's enhanced EventQueue will easily be able to solve. Suppose, for example, that an application needs to modify user-interface components while waiting for a socket to become available. Up to now, because reading data from a socket requires a separate thread, it has been necessary for the application developer to synchronize all modifications to UI components by hand. This operation requires a fair amount of knowledge not only about the state of the application writer's code, but also about the toolkit being used (in this case, Swing).

In designing Swing, we decided that familiarity with those kinds of technicalities placed too many requirements on our target developers. For example, Visual Basic programmers could have a difficult time using Swing components if understanding threads at a deep level were necessary. By synchronizing the threads using an enhanced EventQueue, Swing greatly simplifies the developer's tasks. For example, when an application has to access the user interface from a separate thread, the developer uses a method named invokeLater(), which causes code to be executed on the UI thread without any further need for intervention on the part of the executing application.

Suppose, for instance, that an application needs to update the enable or disable state of a set of controls. The application developer might decide to update these states constantly as the model behind the controls changes. But a less resource-intensive technique is to modify the state of the controls after each event. Up to now, the developer has not been able add a listener to the event queue that is run after each event is dispatched. Swing's event-queue mechanism, which includes the invokeLater() method, now makes such a strategy possible.

These problems center mainly on the need to be able to poll the event-dispatching thread safely. Support for safe polling should guarantee that application polling predicates -- for example, isMySocketDataAvailable() -- are called frequently enough, and that both the polling predicate and the code it calls run on the event-dispatching thread.


The Features

Features of the event-queue mechanism being developed for Swing include the ability to:


The Specification

EventQueue is being extended to implement some new methods. Additionally, two new interfaces and one new class are being added:

public class EventQueue implements Runnable {
    /* Current EventQueue methods */
    public EventQueue()
    public synchronized void postEvent(AWTEvent theEvent);
    public synchronized AWTEvent getNextEvent() throws InterruptedException;
    public synchronized AWTEvent peekEvent();
    public synchronized AWTEvent peekEvent(int id);

    /**
     * New constructor to allow an event queue to be created without
     * automatically creating a seperate dispatch thread.
     */
    public EventQueue(boolean createDispatchThread);

    /** Returns the EventQueue for the current ThreadGroup */
    public static EventQueue getEventQueue(){}

    /** 
     * Allows an object to search or modify the EventQueue's Vector of
     * outstanding events. 
     */
    public synchronized Vector getEvents(){}
  
    /** 
     * This method is called to process each Event as the EventQueue
     * removes it from its queue.
     */
    public void dispatchEvent(AWTEvent event) {}

    /** 
     * Runnable interface method implemented to process Events as they
     * appear in the queue.
     */
    public void run(){}
    
    /**
     * Stops the EventQueue once control returns from processing the
     * current Event.
     */
    public synchronized public void stopRunning() {}
    
    public void addEventQueueListener(EventQueueListener ell) {}
    public void removeEventQueueListener(EventQueueListener ell) {}
    /**
     * Causes <i>request</i> to have its run() method called in the dispatch
     * thread of the EventQueue.  This will happen after all pending
     * events are processed.
     */
    public void invokeLater(Runnable request);

    /**
     * Causes <i>request</i> to have its run() method called in the dispatch
     * thread of the EventQueue.  This will happen after all pending
     * events are processed.  The call blocks until this has happened.  
     * This is <b>bad thing (tm)</b> to do from within the dispatch 
     * thread of the EventQueue.
     */
    public void invokeAndWait(Runnable request) 
        throws InterruptedException, InvocationTargetException;}
}

/**
 * The listener interface for receiving EventQueue events.  Objects
 * can register to receive notifications at various stages of
 * event processing
 */
public interface EventQueueListener extends java.util.EventListener{
    public void willProcessEvent(EventQueueEvent e);
    public void didProcessEvent(EventQueueEvent e);
}

public class EventQueueEvent extends AWTEvent {
    /** Returns he event that is being processed */
    public AWTEvent getEvent();
}
/**
 * This event class can be used to cause code to be executed on the
 * queue's dispatch thread.
 * @see EventQueue#invokeLater
 * @see EventQueue#invokeAndWait
 */
public class WorkEvent extends AWTEvent {
    /** Creates a WorkEvent */
    WorkEvent(Object src, Runnable request);
    
    /** Returns the Runnable associated with the event*/
    Runnable getRunnable();
}


The Scenarios

Let's consider two of the above problems and demonstrate how this new functionality could be used to solve them:

Automatic Repaint Batching

JComponent could be modified such that its repaint() method uses the new DirtyRegionManager to register a shape that needs painting. As processing occurs, many components might register many such shapes with the DirtyRegionManager. This manager would have added itself as an EventQueueListener. In its didProcessEvent() method, it would flush (and coalesce) all the outstanding repaint requests.

A Timer that Fires Synchronously with the EventQueue

A Timer class that would use a separate thread to wait for could be created. When the correct amount of time passes, it could post a subclass of ActiveEvent to its EventQueue. The dispatch of this event would notify the Timer's listeners.

Potential Problems

This scenario has a few potential problems. Two are caused by its implicit ThreadGroup-to-EventQueue mapping. To understand the background behind these problems, you need to know that a method named java.awt.Toolkit.getSystemEventQueue() is defined to return the system event queue -- that is, a queue that contains all events in the entire VM. This is obviously more difficult if the events are being forked off to separate queues. It is not clear if this semantic is worthy of being supported in the future, because security ramifications might limit its utility. There is also some talk of individual VM implementers actually implementing the queues on a per-applet basis, so this semantic may not even be universally supported now.

The second issue is caused by the class java.awt.peer.ActiveEvent. This is an event that dispatches itself without a component as its source. There is some code in a private class named EventDispatchThread that knows about this type of event and manages it using special handling. For the most part, an event can be mapped to ThreadGroups (and therefore EventQueues) by using the component that acts as the event's source. The basic logic of this approach takes Component-to-Window and Window to the ThreadGroup that it was created within.

More APIs can be added if that assumption is not always sufficient. But ActiveEvents have no explicit component to use for this mapping. The API they provide is very useful, if the correct EventQueue is already known. Their use by peers could be replaced by some type of event that has an explicit component that may or may not act as the event's source, but could be used to determine the proper EventQueue.

Arrows


Version 0.5. Last modified 09/30/97.
Copyright © 1995-97 Sun Microsystems, Inc. All Rights Reserved.

Sun's Home Page