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 Red Hat, Inc. and/or its affiliates, and individual
 * contributors as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xnio.channels;

import static java.lang.Thread.currentThread;
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 static org.xnio.Bits.allAreClear;
import static org.xnio.Bits.allAreSet;
import static org.xnio.Bits.anyAreSet;
import static org.xnio.Bits.intBitMask;
import static org.xnio._private.Messages.msg;

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.XnioIoThread;
import org.xnio.XnioWorker;

/**
 * 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
 *
 * @deprecated This class is deprecated; use conduits instead.
 */
@Deprecated
public abstract class TranslatingSuspendableChannel implements SuspendableChannel, WrappedChannel, ReadListenerSettable, WriteListenerSettable, CloseListenerSettable {

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

    private ChannelListener readListener;
    private ChannelListener writeListener;
    private ChannelListener closeListener;

    @SuppressWarnings("unused")
    private volatile int state;
    @SuppressWarnings("unused")
    private volatile Thread readWaiter;
    @SuppressWarnings("unused")
    private volatile Thread writeWaiter;

    @SuppressWarnings("rawtypes")
    private static final AtomicIntegerFieldUpdater stateUpdater = AtomicIntegerFieldUpdater.newUpdater(TranslatingSuspendableChannel.class, "state");

    @SuppressWarnings("rawtypes")
    private static final AtomicReferenceFieldUpdater readWaiterUpdater = AtomicReferenceFieldUpdater.newUpdater(TranslatingSuspendableChannel.class, Thread.class, "readWaiter");
    @SuppressWarnings("rawtypes")
    private static final AtomicReferenceFieldUpdater writeWaiterUpdater = AtomicReferenceFieldUpdater.newUpdater(TranslatingSuspendableChannel.class, Thread.class, "writeWaiter");

    // 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 31 events
    private static final int READ_SINGLE_EXT        = 1 << 0x0B; // one external event count value

    @SuppressWarnings("unused")
    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;

    @SuppressWarnings("unused")
    private static final int WRITE_FLAGS            = intBitMask(0x10, 0x1F);

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

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

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

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

    private final ChannelListener delegateCloseListener = 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 msg.nullParameter("channel");
        }
        this.channel = channel;
        channel.getReadSetter().set(delegateReadListener);
        channel.getWriteSetter().set(delegateWriteListener);
        channel.getCloseSetter().set(delegateCloseListener);
    }

    /**
     * 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 = readListener;
            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 = writeListener;
            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(), closeListener);
    }

    // --- read ---

    /**
     * Indicate that the channel is definitely immediately readable, regardless of the underlying channel state.
     */
    protected void setReadReady() {
        int oldState = setFlags(READ_READY);
        unparkReadWaiters();
        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);
        unparkWriteWaiters();
        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;
    }

    public void setReadListener(final ChannelListener readListener) {
        this.readListener = readListener;
    }

    public ChannelListener getReadListener() {
        return readListener;
    }

    public void setWriteListener(final ChannelListener writeListener) {
        this.writeListener = writeListener;
    }

    public ChannelListener getWriteListener() {
        return writeListener;
    }

    public void setCloseListener(final ChannelListener closeListener) {
        this.closeListener = closeListener;
    }

    public ChannelListener getCloseListener() {
        return closeListener;
    }

    /** {@inheritDoc} */
    public ChannelListener.Setter getCloseSetter() {
        return new CloseListenerSettable.Setter(this);
    }

    /** {@inheritDoc} */
    public ChannelListener.Setter getReadSetter() {
        return new ReadListenerSettable.Setter(this) ;
    }

    /** {@inheritDoc} */
    public ChannelListener.Setter getWriteSetter() {
        return new WriteListenerSettable.Setter(this);
    }

    /** {@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() {
        if (anyAreSet(state, READ_SHUT_DOWN)) {
            return;
        }
        setFlags(READ_REQUESTED);
        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() {
        if (anyAreSet(state, WRITE_SHUT_DOWN)) {
            return;
        }
        setFlags(WRITE_REQUESTED);
        channel.wakeupWrites();
        unparkWriteWaiters();
    }

    /** {@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(), closeListener);
        }
        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(), closeListener);
                }
            }
        }
    }

    /**
     * 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);
    }

    /**
     * Perform the write shutdown action if it hasn't been performed already.
     *
     * @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;
        }
        final Thread thread = currentThread();
        final Thread next = readWaiterUpdater.getAndSet(this, thread);
        try {
            if (anyAreSet(oldState = state, READ_READY | READ_SHUT_DOWN)) {
                return;
            }
            if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
                channel.resumeWrites();
            } else {
                channel.resumeReads();
            }
            park(this);
            if (thread.isInterrupted()) {
                throw msg.interruptedIO();
            }
        } finally {
            // always unpark because we cannot know if our awaken was spurious
            if (next != null) unpark(next);
        }
    }

    /** {@inheritDoc} */
    public void awaitReadable(final long time, final TimeUnit timeUnit) throws IOException {
        int oldState = state;
        if (anyAreSet(oldState, READ_READY | READ_SHUT_DOWN)) {
            return;
        }
        final Thread thread = currentThread();
        final Thread next = readWaiterUpdater.getAndSet(this, thread);
        long duration = timeUnit.toNanos(time);
        try {
            if (anyAreSet(oldState = state, READ_READY | READ_SHUT_DOWN)) {
                return;
            }
            if (allAreSet(oldState, READ_REQUIRES_WRITE)) {
                channel.resumeWrites();
            } else {
                channel.resumeReads();
            }
            parkNanos(this, duration);
            if (thread.isInterrupted()) {
                throw msg.interruptedIO();
            }
        } finally {
            // always unpark because we cannot know if our awaken was spurious
            if (next != null) unpark(next);
        }
    }

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

    /** {@inheritDoc} */
    public void awaitWritable() throws IOException {
        int oldState = state;
        if (anyAreSet(oldState, WRITE_READY | WRITE_SHUT_DOWN)) {
            return;
        }
        final Thread thread = currentThread();
        final Thread next = writeWaiterUpdater.getAndSet(this, thread);
        try {
            if (anyAreSet(oldState = state, WRITE_READY | WRITE_SHUT_DOWN)) {
                return;
            }
            if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
                channel.resumeReads();
            } else {
                channel.resumeWrites();
            }
            park(this);
            if (thread.isInterrupted()) {
                throw msg.interruptedIO();
            }
        } finally {
            // always unpark because we cannot know if our awaken was spurious
            if (next != null) unpark(next);
        }
    }

    /** {@inheritDoc} */
    public void awaitWritable(final long time, final TimeUnit timeUnit) throws IOException {
        int oldState = state;
        if (anyAreSet(oldState, WRITE_READY | WRITE_SHUT_DOWN)) {
            return;
        }
        final Thread thread = currentThread();
        final Thread next = writeWaiterUpdater.getAndSet(this, thread);
        long duration = timeUnit.toNanos(time);
        try {
            if (anyAreSet(oldState = state, WRITE_READY | WRITE_SHUT_DOWN)) {
                return;
            }
            if (allAreSet(oldState, WRITE_REQUIRES_READ)) {
                channel.resumeReads();
            } else {
                channel.resumeWrites();
            }
            parkNanos(this, duration);
            if (thread.isInterrupted()) {
                throw msg.interruptedIO();
            }
        } finally {
            // always unpark because we cannot know if our awaken was spurious
            if (next != null) unpark(next);
        }
    }

    private void unparkReadWaiters() {
        final Thread waiter = readWaiterUpdater.getAndSet(this, null);
        if (waiter != null) {
            unpark(waiter);
        }
    }

    private void unparkWriteWaiters() {
        final Thread waiter = writeWaiterUpdater.getAndSet(this, null);
        if (waiter != null) {
            unpark(waiter);
        }
    }

    @Deprecated
    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(), closeListener);
        }
    }

    /**
     * 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 XnioIoThread getIoThread() {
        return channel.getIoThread();
    }

    /** {@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);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy