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

org.xnio.channels.TranslatingSuspendableChannel Maven / Gradle / Ivy

There is a newer version: 3.8.16.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.xnio.channels;

import static java.lang.Thread.currentThread;
import static java.util.Arrays.copyOf;
import static java.util.concurrent.locks.LockSupport.park;
import static java.util.concurrent.locks.LockSupport.parkNanos;
import static java.util.concurrent.locks.LockSupport.unpark;

import java.io.IOException;
import java.nio.channels.Channel;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.IoUtils;
import org.xnio.Option;
import org.xnio.XnioExecutor;
import org.xnio.XnioWorker;

import static org.xnio.Bits.*;

/**
 * An abstract wrapped channel.
 *
 * @param  the channel type implemented by this class
 * @param  the channel type being wrapped by this class
 *
 * @author David M. Lloyd
 */
@SuppressWarnings("unused")
public abstract class TranslatingSuspendableChannel implements SuspendableChannel, WrappedChannel {

    /**
     * The wrapped channel.
     */
    protected final W channel;

    private final ChannelListener.SimpleSetter readSetter = new ChannelListener.SimpleSetter();
    private final ChannelListener.SimpleSetter writeSetter = new ChannelListener.SimpleSetter();
    private final ChannelListener.SimpleSetter closeSetter = new ChannelListener.SimpleSetter();

    private volatile int state;
    private volatile Thread[] readWaiters = NOT_WAITING;
    private volatile Thread[] writeWaiters = NOT_WAITING;

    private static final Thread[] NOT_WAITING = new Thread[0];

    private static final AtomicIntegerFieldUpdater stateUpdater = AtomicIntegerFieldUpdater.newUpdater(TranslatingSuspendableChannel.class, "state");
    private static final AtomicReferenceFieldUpdater readWaitersUpdater = AtomicReferenceFieldUpdater.newUpdater(TranslatingSuspendableChannel.class, Thread[].class, "readWaiters");
    private static final AtomicReferenceFieldUpdater writeWaitersUpdater = AtomicReferenceFieldUpdater.newUpdater(TranslatingSuspendableChannel.class, Thread[].class, "writeWaiters");

    // read-side

    private static final int READ_REQUESTED         = 1 << 0x00; // user wants to be notified on read
    private static final int READ_REQUIRES_WRITE    = 1 << 0x01; // channel cannot be read due to pending write
    private static final int READ_READY             = 1 << 0x02; // channel is always ready to be read
    private static final int READ_SHUT_DOWN         = 1 << 0x03; // user shut down reads

    private static final int READ_REQUIRES_EXT      = intBitMask(0x0B, 0x0F); // channel cannot be read until external event completes, up to 32 events
    private static final int READ_SINGLE_EXT        = 1 << 0x0B; // one external event count value

    private static final int READ_FLAGS             = intBitMask(0x00, 0x0F);

    // write-side

    private static final int WRITE_REQUESTED        = 1 << 0x10;
    private static final int WRITE_REQUIRES_READ    = 1 << 0x11;
    private static final int WRITE_READY            = 1 << 0x12;
    private static final int WRITE_SHUT_DOWN        = 1 << 0x13; // user requested shut down of writes
    private static final int WRITE_COMPLETE         = 1 << 0x14; // flush acknowledged full write shutdown

    private static final int WRITE_REQUIRES_EXT     = intBitMask(0x1B, 0x1F); // up to 32 events
    private static final int WRITE_SINGLE_EXT       = 1 << 0x1B;

    private static final int WRITE_FLAGS            = intBitMask(0x10, 0x1F);

    private final ChannelListener readListener = new ChannelListener() {
        public void handleEvent(final Channel channel) {
            handleReadable();
        }

        public String toString() {
            return "Read listener for " + TranslatingSuspendableChannel.this;
        }
    };

    private final ChannelListener writeListener = new ChannelListener() {
        public void handleEvent(final Channel channel) {
            handleWritable();
        }

        public String toString() {
            return "Write listener for " + TranslatingSuspendableChannel.this;
        }
    };

    private final ChannelListener closeListener = new ChannelListener() {
        public void handleEvent(final Channel channel) {
            IoUtils.safeClose(TranslatingSuspendableChannel.this);
        }

        public String toString() {
            return "Close listener for " + TranslatingSuspendableChannel.this;
        }
    };

    /**
     * Construct a new instance.
     *
     * @param channel the channel being wrapped
     */
    protected TranslatingSuspendableChannel(final W channel) {
        if (channel == null) {
            throw new IllegalArgumentException("channel is null");
        }
        this.channel = channel;
        channel.getReadSetter().set(readListener);
        channel.getWriteSetter().set(writeListener);
        channel.getCloseSetter().set(closeListener);
    }

    /**
     * Called when the underlying channel is readable.
     */
    protected void handleReadable() {
        int oldState;
        oldState = clearFlags(WRITE_REQUIRES_READ);
        if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
            unparkWriteWaiters();
            if (allAreSet(oldState, WRITE_REQUESTED)) {
                channel.wakeupWrites();
            }
        }
        if (allAreClear(oldState, READ_READY) && anyAreSet(oldState, READ_REQUIRES_WRITE | READ_REQUIRES_EXT)) {
            channel.suspendReads();
            oldState = state;
            if (anyAreSet(oldState, READ_READY) || allAreClear(oldState, READ_REQUIRES_WRITE | READ_REQUIRES_EXT)) {
                // undo
                channel.resumeReads();
            } else {
                return;
            }
        }
        do {
            if (anyAreSet(oldState, READ_SHUT_DOWN)) {
                channel.suspendReads();
                return;
            }
            if (allAreClear(oldState, READ_REQUESTED)) {
                channel.suspendReads();
                oldState = state;
                if (allAreSet(oldState, READ_REQUESTED)) {
                    // undo
                    channel.resumeReads();
                } else {
                    return;
                }
            }
            unparkReadWaiters();
            final ChannelListener listener = readSetter.get();
            if (listener == null) {
                // damage control
                oldState = clearFlag(READ_REQUESTED | WRITE_REQUIRES_READ) & ~READ_REQUESTED;
            } else {
                ChannelListeners.invokeChannelListener(thisTyped(), listener);
                oldState = clearFlags(WRITE_REQUIRES_READ);
            }
            if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
                unparkWriteWaiters();
                // race is OK
                channel.wakeupWrites();
            }
        } while (allAreSet(oldState, READ_READY));
    }

    /**
     * Called when the underlying channel is writable.
     */
    protected void handleWritable() {
        int oldState;
        oldState = clearFlags(READ_REQUIRES_WRITE);
        if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
            unparkReadWaiters();
            if (allAreSet(oldState, READ_REQUESTED)) {
                channel.wakeupReads();
            }
        }
        if (allAreClear(oldState, WRITE_READY) && anyAreSet(oldState, WRITE_REQUIRES_READ | WRITE_REQUIRES_EXT)) {
            channel.suspendWrites();
            oldState = state;
            if (anyAreSet(oldState, WRITE_READY) || allAreClear(oldState, WRITE_REQUIRES_READ | WRITE_REQUIRES_EXT)) {
                // undo
                channel.resumeWrites();
            } else {
                return;
            }
        }
        do {
            if (anyAreSet(oldState, WRITE_COMPLETE)) {
                channel.suspendWrites();
                return;
            }
            if (allAreClear(oldState, WRITE_REQUESTED)) {
                channel.suspendWrites();
                oldState = state;
                if (allAreSet(oldState, WRITE_REQUESTED)) {
                    // undo
                    channel.resumeWrites();
                } else {
                    return;
                }
            }
            unparkWriteWaiters();
            final ChannelListener listener = writeSetter.get();
            if (listener == null) {
                // damage control
                oldState = clearFlags(WRITE_REQUESTED | READ_REQUIRES_WRITE) & ~WRITE_REQUESTED;
            } else {
                ChannelListeners.invokeChannelListener(thisTyped(), listener);
                oldState = clearFlags(READ_REQUIRES_WRITE);
            }
            if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
                unparkReadWaiters();
                // race is OK
                channel.wakeupReads();
            }
        } while (allAreSet(oldState, WRITE_READY));
    }

    /**
     * Called when the underlying channel is closed.
     *
     */
    protected void handleClosed() {
        ChannelListeners.invokeChannelListener(thisTyped(), closeSetter.get());
    }

    // --- read ---

    /**
     * Indicate that the channel is definitely immediately readable, regardless of the underlying channel state.
     */
    protected void setReadReady() {
        int oldState = setFlags(READ_READY);
        if (allAreSet(oldState, READ_READY)) {
            // idempotent
            return;
        }
        if (allAreSet(oldState, READ_REQUESTED) && anyAreSet(oldState, READ_REQUIRES_EXT | READ_REQUIRES_WRITE)) {
            // wakeup is required to proceed
            channel.wakeupReads();
        }
    }

    /**
     * Indicate that the channel is no longer definitely immediately readable.
     */
    protected void clearReadReady() {
        int oldState = clearFlags(READ_READY);
        if (allAreClear(oldState, READ_READY)) {
            // idempotent
            return;
        }
        if (!allAreClear(oldState, READ_REQUESTED) && !anyAreSet(oldState, READ_REQUIRES_EXT | READ_REQUIRES_WRITE)) {
            // we can read again when the underlying channel is ready
            channel.resumeReads();
        }
    }

    /**
     * Indicate that the channel will not be readable until the write handler is called.
     */
    protected void setReadRequiresWrite() {
        int oldState = setFlags(READ_REQUIRES_WRITE);
        if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
            // not the first caller
            return;
        }
        if (allAreClear(oldState, READ_READY | READ_REQUIRES_EXT)) {
            // read cannot proceed until write does
            channel.resumeWrites();
        }
    }

    /**
     * Indicate if the channel is not readable until the write handler is called.
     */
    protected boolean readRequiresWrite() {
        return allAreSet(state, READ_REQUIRES_WRITE);
    }

    /**
     * Indicate that the channel no longer requires writability for reads to proceed.
     */
    protected void clearReadRequiresWrite() {
        int oldState = clearFlags(READ_REQUIRES_WRITE);
        if (allAreClear(oldState, READ_REQUIRES_WRITE)) {
            // idempotent
            return;
        }
        if (allAreClear(oldState, READ_REQUIRES_EXT) && allAreSet(oldState, READ_REQUESTED)) {
            if (allAreSet(oldState, READ_READY)) {
                channel.wakeupReads();
            } else {
                channel.resumeReads();
            }
        }
    }

    /**
     * Indicate that read requires an external task to complete.
     *
     * @return {@code true} if the flag was set, {@code false} if too many tasks are already outstanding
     */
    protected boolean tryAddReadRequiresExternal() {
        int oldState = addFlag(READ_REQUIRES_EXT, READ_SINGLE_EXT);
        return (oldState & READ_REQUIRES_EXT) != READ_REQUIRES_EXT;
    }

    /**
     * Indicate that one external read task was completed.  This method should be called once for every time
     * that {@link #tryAddReadRequiresExternal()} returned {@code true}.
     */
    protected void removeReadRequiresExternal() {
        clearFlag(READ_SINGLE_EXT);
    }

    /**
     * Set the channel read shut down flag.
     *
     * @return {@code true} if the channel has fully closed due to this call, {@code false} otherwise
     */
    protected boolean setReadShutDown() {
        return (setFlags(READ_SHUT_DOWN) & (READ_SHUT_DOWN | WRITE_SHUT_DOWN)) == WRITE_SHUT_DOWN;
    }

    // --- write ---

    /**
     * Indicate that the channel is definitely immediately writable, regardless of the underlying channel state.
     */
    protected void setWriteReady() {
        int oldState = setFlags(WRITE_READY);
        if (allAreSet(oldState, WRITE_READY)) {
            // idempotent
            return;
        }
        if (allAreSet(oldState, WRITE_REQUESTED) && anyAreSet(oldState, WRITE_REQUIRES_EXT | WRITE_REQUIRES_READ)) {
            // wakeup is required to proceed
            channel.wakeupWrites();
        }
    }

    /**
     * Indicate that the channel is no longer definitely immediately writable.
     */
    protected void clearWriteReady() {
        int oldState = clearFlags(WRITE_READY);
        if (allAreClear(oldState, WRITE_READY)) {
            // idempotent
            return;
        }
        if (!allAreClear(oldState, WRITE_REQUESTED) && ! anyAreSet(oldState, WRITE_REQUIRES_EXT | WRITE_REQUIRES_READ)) {
            // we can write again when the underlying channel is ready
            channel.resumeWrites();
        }
    }

    /**
     * Indicate that the channel will not be writable until the read handler is called.
     */
    protected void setWriteRequiresRead() {
        int oldState = setFlags(WRITE_REQUIRES_READ);
        if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
            // not the first caller
            return;
        }
        if (allAreClear(oldState, WRITE_READY | WRITE_REQUIRES_EXT)) {
            // write cannot proceed until read does
            channel.resumeReads();
        }
    }

    /**
     * Indicate if the channel is not writable until the read handler is called.
     */
    protected boolean writeRequiresRead() {
        return allAreSet(state, WRITE_REQUIRES_READ);
    }

    /**
     * Indicate that the channel no longer requires writability for writes to proceed.
     */
    protected void clearWriteRequiresRead() {
        int oldState = clearFlags(WRITE_REQUIRES_READ);
        if (allAreClear(oldState, WRITE_REQUIRES_READ)) {
            // idempotent
            return;
        }
        if (allAreClear(oldState, WRITE_REQUIRES_EXT) && allAreSet(oldState, WRITE_REQUESTED)) {
            if (allAreSet(oldState, WRITE_READY)) {
                channel.wakeupWrites();
            } else {
                channel.resumeWrites();
            }
        }
    }

    /**
     * Indicate that write requires an external task to complete.
     *
     * @return {@code true} if the flag was set, {@code false} if too many tasks are already outstanding
     */
    protected boolean tryAddWriteRequiresExternal() {
        int oldState = addFlag(WRITE_REQUIRES_EXT, WRITE_SINGLE_EXT);
        return (oldState & WRITE_REQUIRES_EXT) != WRITE_REQUIRES_EXT;
    }

    /**
     * Indicate that one external write task was completed.  This method should be called once for every time
     * that {@link #tryAddWriteRequiresExternal()} returned {@code true}.
     */
    protected void removeWriteRequiresExternal() {
        clearFlag(WRITE_SINGLE_EXT);
    }

    /**
     * Set the channel write shut down flag.
     *
     * @return {@code true} if the channel has fully closed due to this call, {@code false} otherwise
     */
    protected boolean setWriteShutDown() {
        return (setFlags(WRITE_SHUT_DOWN) & (WRITE_SHUT_DOWN | READ_SHUT_DOWN)) == READ_SHUT_DOWN;
    }

    // --- read & write ---

    /**
     * Set both the channel read and write shut down flags.
     *
     * @return {@code true} if the channel has fully closed (for the first time) due to this call, {@code false} otherwise
     */
    protected boolean setClosed() {
        return (setFlags(READ_SHUT_DOWN | WRITE_SHUT_DOWN) & (READ_SHUT_DOWN | WRITE_SHUT_DOWN)) != (READ_SHUT_DOWN | WRITE_SHUT_DOWN);
    }

    /**
     * Get this channel, cast to the implemented channel type.
     *
     * @return {@code this}
     */
    @SuppressWarnings("unchecked")
    protected final C thisTyped() {
        return (C) this;
    }

    /** {@inheritDoc} */
    public ChannelListener.Setter getCloseSetter() {
        return closeSetter;
    }

    /** {@inheritDoc} */
    public ChannelListener.Setter getReadSetter() {
        return readSetter;
    }

    /** {@inheritDoc} */
    public ChannelListener.Setter getWriteSetter() {
        return writeSetter;
    }

    /** {@inheritDoc} */
    public void suspendReads() {
        clearFlags(READ_REQUESTED);
        // let the read/write handler actually suspend, to avoid awkward race conditions
    }

    /** {@inheritDoc} */
    public void resumeReads() {
        final int oldState = setFlags(READ_REQUESTED);
        if (anyAreSet(oldState, READ_REQUESTED | READ_SHUT_DOWN)) {
            // idempotent or shut down, either way
            return;
        }
        if (allAreSet(oldState, READ_READY)) {
            // reads are known to be ready so trigger listener right away
            channel.wakeupReads();
            return;
        }
        if (allAreClear(oldState, READ_REQUIRES_EXT)) {
            if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
                channel.resumeWrites();
            } else {
                channel.resumeReads();
            }
        }
    }

    public boolean isReadResumed() {
        return allAreSet(state, READ_REQUESTED);
    }

    /** {@inheritDoc} */
    public void wakeupReads() {
        final int oldState = setFlags(READ_REQUESTED);
        if (anyAreSet(oldState, READ_SHUT_DOWN)) {
            return;
        }
        channel.wakeupReads();
    }

    /** {@inheritDoc} */
    public void suspendWrites() {
        clearFlags(WRITE_REQUESTED);
        // let the read/write handler actually suspend, to avoid awkward race conditions
    }

    /** {@inheritDoc} */
    public void resumeWrites() {
        final int oldState = setFlags(WRITE_REQUESTED);
        if (anyAreSet(oldState, WRITE_REQUESTED | WRITE_COMPLETE)) {
            // idempotent or shut down, either way
            return;
        }
        if (allAreSet(oldState, WRITE_READY)) {
            // reads are known to be ready so trigger listener right away
            channel.wakeupWrites();
            return;
        }
        if (allAreClear(oldState, WRITE_REQUIRES_EXT)) {
            if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
                channel.resumeReads();
            } else {
                channel.resumeWrites();
            }
        }
    }

    public boolean isWriteResumed() {
        return allAreSet(state, WRITE_REQUESTED);
    }

    /** {@inheritDoc} */
    public void wakeupWrites() {
        final int oldState = setFlags(WRITE_REQUESTED);
        if (anyAreSet(oldState, WRITE_COMPLETE)) {
            return;
        }
        channel.wakeupWrites();
    }

    /** {@inheritDoc} */
    public boolean supportsOption(final Option option) {
        return channel.supportsOption(option);
    }

    /** {@inheritDoc} */
    public  T getOption(final Option option) throws IOException {
        return channel.getOption(option);
    }

    /** {@inheritDoc} */
    public  T setOption(final Option option, final T value) throws IllegalArgumentException, IOException {
        return channel.setOption(option, value);
    }

    /**
     * Perform channel flush.  To change the action taken to flush, subclasses should override {@link #flushAction(boolean)}.
     *
     * @return {@code true} if the flush completed, or {@code false} if the operation would block
     * @throws IOException if an error occurs
     */
    public final boolean flush() throws IOException {
        int oldState, newState;
        oldState = stateUpdater.get(this);
        if (allAreSet(oldState, WRITE_COMPLETE)) {
            return channel.flush();
        }
        final boolean shutDown = allAreSet(oldState, WRITE_SHUT_DOWN);
        if (! flushAction(shutDown)) {
            return false;
        }
        if (! shutDown) {
            return true;
        }
        newState = oldState | WRITE_COMPLETE;
        while (! stateUpdater.compareAndSet(this, oldState, newState)) {
            oldState = stateUpdater.get(this);
            if (allAreSet(oldState, WRITE_COMPLETE)) {
                return channel.flush();
            }
            newState = oldState | WRITE_COMPLETE;
        }
        final boolean readShutDown = allAreSet(oldState, READ_SHUT_DOWN);
        try {
            shutdownWritesComplete(readShutDown);
        } finally {
            if (readShutDown) ChannelListeners.invokeChannelListener(thisTyped(), closeSetter.get());
        }
        return channel.flush();
    }

    /**
     * The action to perform when the channel is flushed.  By default, this method delegates to the underlying channel.
     * If the {@code shutDown} parameter is set, and this method returns {@code true}, the underlying channel will be
     * shut down and this method will never be called again (future calls to {@link #flush()} will flush the underlying
     * channel until it returns {@code true}).
     *
     * @param shutDown {@code true} if the channel's write side has been shut down, {@code false} otherwise
     * @return {@code true} if the flush succeeded, {@code false} if it would block
     * @throws IOException if an error occurs
     */
    protected boolean flushAction(final boolean shutDown) throws IOException {
        return channel.flush();
    }

    /**
     * Notification that the channel has successfully flushed after having shut down writes.  The underlying
     * channel may not yet be fully flushed at this time.
     *
     * @param readShutDown {@code true} if the read side was already shut down, {@code false} otherwise
     * @throws IOException if an error occurs
     */
    protected void shutdownWritesComplete(final boolean readShutDown) throws IOException {
    }

    /**
     * Perform the read shutdown action if it hasn't been performed already.
     *
     * @throws IOException if an I/O error occurs
     */
    public void shutdownReads() throws IOException {
        int old = setFlags(READ_SHUT_DOWN);
        if (allAreClear(old, READ_SHUT_DOWN)) {
            final boolean writeComplete = allAreSet(old, WRITE_COMPLETE);
            try {
                shutdownReadsAction(writeComplete);
            } finally {
                if (writeComplete) {
                    ChannelListeners.invokeChannelListener(thisTyped(), closeSetter.get());
                }
            }
        }
    }

    /**
     * The action to perform when reads are shut down.  By default, this method delegates to the underlying channel.
     *
     * @throws IOException if an error occurs
     * @param writeComplete
     */
    protected void shutdownReadsAction(final boolean writeComplete) throws IOException {
        channel.shutdownReads();
    }

    /**
     * Determine whether the channel is shut down for reads.
     *
     * @return whether the channel is shut down for reads
     */
    protected boolean isReadShutDown() {
        return allAreSet(state, READ_SHUT_DOWN);
    }

    /**
     * Base implementation method which simply delegates the shutdown request to the delegate channel.  Subclasses may
     * override this method and call up to this method as appropriate.
     *
     * @return {@code true} if the channel was shut down, or {@code false} if the operation would block
     * @throws IOException if an I/O error occurs
     */
    public void shutdownWrites() throws IOException {
        int old = setFlags(WRITE_SHUT_DOWN);
        if (allAreClear(old, WRITE_SHUT_DOWN)) {
            shutdownWritesAction();
        }
    }

    /**
     * The action to perform when writes are requested to be shut down.  By default, this method delegates to the
     * underlying channel.
     *
     * @throws IOException if an error occurs
     */
    protected void shutdownWritesAction() throws IOException {
        channel.shutdownWrites();
    }

    /**
     * Determine whether the channel is shut down for writes.
     *
     * @return whether the channel is shut down for writes
     */
    protected boolean isWriteShutDown() {
        return allAreSet(state, WRITE_SHUT_DOWN);
    }

    protected boolean isWriteComplete() {
        return allAreSet(state, WRITE_COMPLETE);
    }

    /** {@inheritDoc} */
    public void awaitReadable() throws IOException {
        int oldState = state;
        if (anyAreSet(oldState, READ_READY | READ_SHUT_DOWN)) {
            return;
        }
        if (addReadWaiter()) {
            if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
                channel.resumeWrites();
            } else {
                channel.resumeReads();
            }
            park(this);
        }
        return;
    }

    /** {@inheritDoc} */
    public void awaitReadable(final long time, final TimeUnit timeUnit) throws IOException {
        int oldState = state;
        if (anyAreSet(oldState, READ_READY | READ_SHUT_DOWN)) {
            return;
        }
        if (addReadWaiter()) {
            if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
                channel.resumeWrites();
            } else {
                channel.resumeReads();
            }
            parkNanos(this, timeUnit.toNanos(time));
        }
        return;
    }

    public XnioExecutor getReadThread() {
        return channel.getReadThread();
    }

    /** {@inheritDoc} */
    public void awaitWritable() throws IOException {
        int oldState = state;
        if (anyAreSet(oldState, WRITE_READY | WRITE_SHUT_DOWN)) {
            return;
        }
        if (addWriteWaiter()) {
            if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
                channel.resumeReads();
            } else {
                channel.resumeWrites();
            }
            park(this);
        }
        return;
    }

    /** {@inheritDoc} */
    public void awaitWritable(final long time, final TimeUnit timeUnit) throws IOException {
        int oldState = state;
        if (anyAreSet(oldState, WRITE_READY | WRITE_SHUT_DOWN)) {
            return;
        }
        if (addWriteWaiter()) {
            if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
                channel.resumeReads();
            } else {
                channel.resumeWrites();
            }
            parkNanos(this, timeUnit.toNanos(time));
        }
        return;
    }

    public XnioExecutor getWriteThread() {
        return channel.getWriteThread();
    }

    /**
     * Close this channel.  This method is idempotent.
     *
     * @throws IOException if an I/O error occurs
     */
    public void close() throws IOException {
        int old = setFlags(READ_SHUT_DOWN | WRITE_SHUT_DOWN | WRITE_COMPLETE);
        final boolean readShutDown = allAreSet(old, READ_SHUT_DOWN), writeShutDown = allAreSet(old, WRITE_COMPLETE);
        if (! (readShutDown && writeShutDown)) try {
            closeAction(readShutDown, writeShutDown);
        } finally {
            ChannelListeners.invokeChannelListener(thisTyped(), closeSetter.get());
        }
    }

    /**
     * The action to perform when the channel is closed via the {@link #close()} method.  By default, the underlying
     * channel is closed.
     *
     * @param readShutDown if reads were previously shut down
     * @param writeShutDown if writes were previously shut down
     * @throws IOException if an error occurs
     */
    protected void closeAction(final boolean readShutDown, final boolean writeShutDown) throws IOException {
        channel.close();
    }

    /** {@inheritDoc} */
    public boolean isOpen() {
        return ! allAreSet(state, READ_SHUT_DOWN | WRITE_COMPLETE);
    }

    /** {@inheritDoc} */
    public W getChannel() {
        return channel;
    }

    /** {@inheritDoc} */
    public XnioWorker getWorker() {
        return channel.getWorker();
    }

    /** {@inheritDoc} */
    public String toString() {
        return getClass().getName() + " around " + channel;
    }

    // state operations

    private int setFlags(int flags) {
        int oldState;
        do {
            oldState = state;
            if ((oldState & flags) == flags) {
                return oldState;
            }
        } while (! stateUpdater.compareAndSet(this, oldState, oldState | flags));
        return oldState;
    }

    private int clearFlags(int flags) {
        int oldState;
        do {
            oldState = state;
            if ((oldState & flags) == 0) {
                return oldState;
            }
        } while (! stateUpdater.compareAndSet(this, oldState, oldState & ~flags));
        return oldState;
    }

    private int addFlag(final int mask, final int count) {
        int oldState;
        do {
            oldState = state;
            if ((oldState & mask) == mask) {
                return oldState;
            }
        } while (! stateUpdater.compareAndSet(this, oldState, oldState + count));
        return oldState;
    }

    private int clearFlag(final int count) {
        return stateUpdater.getAndAdd(this, -count);
    }

    private boolean addReadWaiter() {
        Thread[] oldWaiters, newWaiters;
        do {
            oldWaiters = readWaiters;
            if (oldWaiters == NOT_WAITING) {
                return false;
            } else if (oldWaiters == null) {
                newWaiters = new Thread[] { currentThread() };
            } else {
                final int oldLength = oldWaiters.length;
                for (int i = 0; i < oldLength; i++) {
                    if (oldWaiters[i] == currentThread()) {
                        return true;
                    }
                }
                newWaiters = copyOf(oldWaiters, oldLength + 1);
                newWaiters[oldLength] = currentThread();
            }
        } while (! readWaitersUpdater.compareAndSet(this, oldWaiters, newWaiters));
        return true;
    }

    private boolean addWriteWaiter() {
        Thread[] oldWaiters, newWaiters;
        do {
            oldWaiters = writeWaiters;
            if (oldWaiters == NOT_WAITING) {
                return false;
            } else if (oldWaiters == null) {
                newWaiters = new Thread[] { currentThread() };
            } else {
                final int oldLength = oldWaiters.length;
                for (int i = 0; i < oldLength; i++) {
                    if (oldWaiters[i] == currentThread()) {
                        return true;
                    }
                }
                newWaiters = copyOf(oldWaiters, oldLength + 1);
                newWaiters[oldLength] = currentThread();
            }
        } while (! writeWaitersUpdater.compareAndSet(this, oldWaiters, newWaiters));
        return true;
    }

    private void unparkReadWaiters() {
        final Thread[] waiters = readWaitersUpdater.getAndSet(this, NOT_WAITING);
        for (Thread waiter : waiters) {
            unpark(waiter);
        }
    }

    private void unparkWriteWaiters() {
        final Thread[] waiters = writeWaitersUpdater.getAndSet(this, NOT_WAITING);
        for (Thread waiter : waiters) {
            unpark(waiter);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy