All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dellroad.muxable.MuxableChannel Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2021 Archie L. Cobbs. All rights reserved.
 */

package org.dellroad.muxable;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.Selector;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.BlockingQueue;

/**
 * A single channel supporting parallel nested byte-oriented channels operating independently.
 *
 * 

* The nested "child" channels are byte-oriented and operate independently, and all scoped to the parent {@link MuxableChannel}. * In other words, if a parent is closed, all of its nested channels are also implicitly closed; on the other hand, * if a nested channel is closed, only that nested channel is affected, and its parent and siblings are unaffected. * *

* More precisely, a nested channel is represented by a {@link NestedChannel}, which provides access to the * input and output channels. When a {@link MuxableChannel} is closed, these two channels are explicitly closed * for all of its existing nested channels (on the other hand, whether the channel(s) that connect to the peer, * if any, are explicitly closed depends on the {@link MuxableChannel} implementation). * *

* Nested channels are bidirectional. Unlike TCP sockets, they do not support shutting down only one direction. * Closing either the input or output channel results in both channels being closed. * *

* Any I/O exception implies brokenness: if a {@link MuxableChannel} or any nested channel throws {@link IOException}, * one should assume it (the channel that threw the exception) is no longer usable and should be closed. * *

* Deadlock Requirements * *

* Implementations may use varying strategies for multiplexing the nested channels over the underlying "real" channel, and * this may have subtle effects on behavior. For example, with implementations that multiplex over a single underlying TCP * stream, there may be situations where, after certain internal buffer limits are reached, further attempts to read from * nested channel A will block unless and until more data is read from a sibling nested channel B, etc. Nested channels * might also block if the {@link BlockingQueue} returned by {@link #getNestedChannelRequests} reaches its internal buffer * capacity, etc. In any case, such restrictions or limitations should be clearly documented by the implementation. * *

* However, all implementations must avoid "senseless deadlock"; more precisely: if, for every nested input channel * along with the {@link BlockingQueue} returned by {@link #getNestedChannelRequests}, there exists some {@link Thread} * or {@link Selector} currently polling for data, and there is any data is available, then at least one must become * readable and provide new data. * *

* Recursive Nesting * *

* An inner {@link MuxableChannel} may be created from a nested channel that itself was created from an outer * {@link MuxableChannel}. This process may be repeated with arbitrary levels of nesting. As always, the closure * of any channel forces the closure of all of its nested channels, and ultimately closing all of its descendents. * * @param input channel type * @param output channel type */ public interface MuxableChannel< I extends SelectableChannel & ReadableByteChannel, O extends SelectableChannel & WritableByteChannel> extends Channel { /** * Create a new nested channel, or pair of nested channels, scoped to this instance. * *

* The remote side will be notified by the appearance of a corresponding {@link NestedChannel} in the queue * returned by {@link #getNestedChannelRequests} (with the input and output channels reversed, of course). * The delivery of requests on the remote side is guaranteed to preserve order (but only to the extent order * is well-defined on the sending side, i.e., there is a "happens before" relationship). * * @param requestData data to supply to the remote side (via {@link NestedChannel#getRequestData}) * @param directions which of input and/or output to create * @return the newly created nested channel(s) * @throws IOException if an I/O error occurs * @throws IllegalArgumentException if either parameter is null */ NestedChannel newNestedChannel(ByteBuffer requestData, Directions directions) throws IOException; /** * Create a pair of nested input and output channels scoped to this instance. * *

* Equivalent to: {@link #newNestedChannel(ByteBuffer, Directions) * newNestedChannel(requestData, Directions.BIDIRECTIONAL)}. * * @param requestData data to supply to the remote side (via {@link NestedChannel#getRequestData}) * @return the newly created nested channel(s) * @throws IOException if an I/O error occurs * @throws IllegalArgumentException if {@code requestData} is null */ default NestedChannel newNestedChannel(ByteBuffer requestData) throws IOException { return this.newNestedChannel(requestData, Directions.BIDIRECTIONAL); } /** * Access the queue of incoming {@link NestedChannel}s initiated from the remote side. * *

* Each {@link NestedChannel} corresponds to a remote invocation of * {@link #newNestedChannel newNestedChannel()}. Moreover, the order of requests is preserved. * *

* Because the remote side can close a nested channel at any time, it is possible that the new channel associated * with an incoming {@link NestedChannel} has already been closed in a subsequent message from the remote peer * by the time the original {@link NestedChannel} is pulled from this queue. In such a case, the nested channel's * input and output streams will throw {@link IOException} on first access. * *

* Note: no special change happens to the returned {@link BlockingQueue} once this instance is closed; * instead, new requests simply stop appearing. Therefore, after closing this instance, any thread(s) * that are blocked polling for new data may need to be woken up via {@link Thread#interrupt}. * * @return queue of {@link NestedChannel} initiated from the remote side */ BlockingQueue> getNestedChannelRequests(); /** * Close this instance. * *

* All outstanding nested channels are also implicitly closed. * * @throws IOException if an I/O error occurs */ @Override void close() throws IOException; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy