Description

Summary

Add three methods to java.nio.channels.Selector to do selection operations that perform an action on the keys of each channel that is ready to perform an operation. The existing select methods add the key to the selector's selected-key set, the new methods are an alternative way to report that a channel is ready to perform an operation.

Problem

Adding keys to, and removing keys from, the selector's selected-key set can be a performance issue in many high performance server applications. The Netty framework is one example that works around this by hacking into private fields of the selector implementations in order to inject its own object to collect events. Such hacks will break when the JDK internals are fully encapsulated.

Solution

Update the Selector API with a new form of selection operation that invokes an action (represented by a Consumer object) on the keys of each channel that is ready to perform an operation. Three methods analogous to the three existing select and selectNow methods are proposed to be added.

Specification

The specdiff for both java.nio.channels.Selector and java.nio.channels.SelectionKey is attached.

For ease of reviewing, the substantive additions, specifically the main addition to the Selector class description and the three new methods, are inlined here:

/**
* <h3>Selection operations that perform an action on selected keys</h3>
*
* <p> During each selection operation, keys may be removed from the selector's
* key, selected-key, and cancelled-key sets. Selection is performed by the
* {@link #select(Consumer)}, {@link #select(Consumer,long)}, and {@link
* #selectNow(Consumer)} methods, and involves three steps: </p>
*
* <ol>
*
* <li><p> Each key in the cancelled-key set is removed from each key set of
* which it is a member, and its channel is deregistered. This step leaves
* the cancelled-key set empty. </p></li>
*
* <li><p> The underlying operating system is queried for an update as to the
* readiness of each remaining channel to perform any of the operations
* identified by its key's interest set as of the moment that the selection
* operation began.
*
* <p> For a channel that is ready for at least one such operation, the
* ready-operation set of the channel's key is set to identify exactly the
* operations for which the channel is ready and the <i>action</i> method
* specified to the {@code select} method is invoked to consume the channel's
* key. Any readiness information previously recorded in the ready set is
* discarded prior to invoking the <i>action</i> method.
*
* <p> Alternatively, where a channel is ready for more than one operation,
* its <i>action</i> method may be invoked more than once with the channel's
* key and ready-operation set modified to a subset of the operations for
* which the channel is ready. Where the <i>action</i> method is invoked more
* than once for the same key then its ready-operation set never contains
* operation bits that were contained in the set at previous calls to the
* <i>action</i> method in the same selection operation. </p></li>
*
* <li><p> If any keys were added to the cancelled-key set while step (2) was
* in progress then they are processed as in step (1). </p></li>
*
* </ol>
*/
/**
* Selects and performs an action on the keys whose corresponding channels
* are ready for I/O operations.
*
* <p> This method performs a blocking <a href="#selop">selection
* operation</a>. It wakes up from querying the operating system only when
* at least one channel is selected, this selector's {@link #wakeup wakeup}
* method is invoked, the current thread is interrupted, or the given
* timeout period expires, whichever comes first.
*
* <p> The specified <i>action</i> is invoked with the key for each channel
* that is ready to perform an operation identified by its key's interest
* set. The <i>action</i> method may be invoked more than once for the same
* key but with the ready-operation set containing a subset of the operations
* for which the channel is ready (as described above).
*
* The <i>action</i> method is invoked while synchronized on the selector
* and its selected-key set. Great care must be taken to avoid deadlocking
* with other threads that also synchronize on these objects.
* Selection operations are not reentrant in general and consequently the
* <i>action</i> should take great care not to attempt a selection operation
* on the same selector. The behavior when attempting a reentrant selection
* operation is implementation specific and therefore not specified. If the
* <i>action</i> method closes the selector then {@code ClosedSelectorException}
* is thrown when the action completes. The <i>action</i> method is not
* prohibited from closing channels registered with the selector, nor
* prohibited from cancelling keys or changing a key's interest set. If a
* channel is selected but its key is cancelled or its interest set changed
* before the <i>action</i> is performed on the key then it is
* implementation specific as to whether the action <i>action</i> is invoked
* (it may be invoked with an {@link SelectionKey#isValid() invalid} key).
* Exceptions thrown by the action are relayed to the caller.
*
* <p> This method does not offer real-time guarantees: It schedules the
* timeout as if by invoking the {@link Object#wait(long)} method.
*
* @implSpec The default implementation removes all keys from the
* selected-key set, invokes {@link #select(long) select(long)} with the
* given timeout and then performs the action for each key added to the
* selected-key set. The default implementation does not detect the action
* performing a reentrant selection operation. The selected-key set may
* or may not be empty on completion of the default implementation.
*
* @param action The action to perform
*
* @param timeout If positive, block for up to {@code timeout}
* milliseconds, more or less, while waiting for a
* channel to become ready; if zero, block indefinitely;
* must not be negative
*
* @return The number of unique keys consumed, possibly zero
*
* @throws IOException
* If an I/O error occurs
*
* @throws ClosedSelectorException
* If this selector is closed or is closed by the action
*
* @throws IllegalArgumentException
* If the value of the timeout argument is negative
*
* @since 11
*/
public int select(Consumer<SelectionKey> action, long timeout)
throws IOException
/**
* Selects and performs an action on the keys whose corresponding channels
* are ready for I/O operations.
*
* <p> This method performs a blocking <a href="#selop">selection
* operation</a>. It wakes up from querying the operating system only when
* at least one channel is selected, this selector's {@link #wakeup wakeup}
* method is invoked, or the current thread is interrupted, whichever comes
* first.
*
* <p> This method is equivalent to invoking the 2-arg
* {@link #select(Consumer, long) select} method with a timeout of {@code 0}
* to block indefinitely. </p>
*
* @implSpec The default implementation invokes the 2-arg {@code select}
* method with a timeout of {@code 0}.
*
* @param action The action to perform
*
* @return The number of unique keys consumed, possibly zero
*
* @throws IOException
* If an I/O error occurs
*
* @throws ClosedSelectorException
* If this selector is closed or is closed by the action
*
* @since 11
*/
public int select(Consumer<SelectionKey> action) throws IOException
/**
* Selects and performs an action on the keys whose corresponding channels
* are ready for I/O operations.
*
* <p> This method performs a non-blocking <a href="#selop">selection
* operation</a>.
*
* <p> Invoking this method clears the effect of any previous invocations
* of the {@link #wakeup wakeup} method. </p>
*
* @implSpec The default implementation removes all keys from the
* selected-key set, invokes {@link #selectNow() selectNow()} and then
* performs the action for each key added to the selected-key set. The
* default implementation does not detect the action performing a reentrant
* selection operation. The selected-key set may or may not be empty on
* completion of the default implementation.
*
* @param action The action to perform
*
* @return The number of unique keys consumed, possibly zero
*
* @throws IOException
* If an I/O error occurs
*
* @throws ClosedSelectorException
* If this selector is closed or is closed by the action
*
* @since 11
*/
public int selectNow(Consumer<SelectionKey> action)
throws IOException