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

org.xnio.AbstractIoFuture Maven / Gradle / Ivy

There is a newer version: 3.8.16.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2008 Red Hat, Inc. and/or its affiliates.
 *
 * 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;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

import static org.xnio._private.Messages.futureMsg;

/**
 * An abstract base class for {@code IoFuture} objects.  Used to easily produce implementations.
 *
 * @param  the type of result that this operation produces
 */
public abstract class AbstractIoFuture implements IoFuture {

    @SuppressWarnings("unchecked")
    private final AtomicReference> stateRef = new AtomicReference<>((State) ST_INITIAL);

    private static final State ST_INITIAL = new InitialState<>();
    private static final State ST_CANCELLED = new CancelledState<>();

    static abstract class State {
        abstract Status getStatus();

        abstract void notifyDone(AbstractIoFuture future, T result);

        abstract void notifyFailed(AbstractIoFuture future, IOException exception);

        abstract void notifyCancelled(AbstractIoFuture future);

        abstract void cancel();

        abstract boolean cancelRequested();

        State withWaiter(final Thread thread) {
            return new WaiterState(this, thread);
        }

         State withNotifier(final Executor executor, final AbstractIoFuture future, final Notifier notifier, final A attachment) {
            return new NotifierState(this, notifier, attachment);
        }

        State withCancelHandler(final Cancellable cancellable) {
            return new CancellableState(this, cancellable);
        }

        T getResult() {
            throw new IllegalStateException();
        }

        IOException getException() {
            throw new IllegalStateException();
        }
    }

    static final class InitialState extends State {

        Status getStatus() {
            return Status.WAITING;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
        }

        void notifyCancelled(final AbstractIoFuture future) {
        }

        void cancel() {
        }

        boolean cancelRequested() {
            return false;
        }
    }

    static final class CompleteState extends State {
        private final T result;

        CompleteState(final T result) {
            this.result = result;
        }

        Status getStatus() {
            return Status.DONE;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
        }

        void notifyCancelled(final AbstractIoFuture future) {
        }

        void cancel() {
        }

        State withCancelHandler(final Cancellable cancellable) {
            return this;
        }

        State withWaiter(final Thread thread) {
            return this;
        }

         State withNotifier(final Executor executor, final AbstractIoFuture future, final Notifier notifier, final A attachment) {
            future.runNotifier(new NotifierRunnable(notifier, future, attachment));
            return this;
        }

        T getResult() {
            return result;
        }

        boolean cancelRequested() {
            return false;
        }
    }

    static final class FailedState extends State {
        private final IOException exception;

        FailedState(final IOException exception) {
            this.exception = exception;
        }

        Status getStatus() {
            return Status.FAILED;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
        }

        void notifyCancelled(final AbstractIoFuture future) {
        }

        void cancel() {
        }

        State withCancelHandler(final Cancellable cancellable) {
            return this;
        }

        State withWaiter(final Thread thread) {
            return this;
        }

         State withNotifier(final Executor executor, final AbstractIoFuture future, final Notifier notifier, final A attachment) {
            future.runNotifier(new NotifierRunnable(notifier, future, attachment));
            return this;
        }

        IOException getException() {
            return exception;
        }

        boolean cancelRequested() {
            return false;
        }
    }

    static final class CancelledState extends State {

        CancelledState() {
        }

        Status getStatus() {
            return Status.CANCELLED;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
        }

        void notifyCancelled(final AbstractIoFuture future) {
        }

        void cancel() {
        }

        State withCancelHandler(final Cancellable cancellable) {
            try {
                cancellable.cancel();
            } catch (Throwable ignored) {}
            return this;
        }

         State withNotifier(final Executor executor, final AbstractIoFuture future, final Notifier notifier, final A attachment) {
            future.runNotifier(new NotifierRunnable(notifier, future, attachment));
            return this;
        }

        State withWaiter(final Thread thread) {
            return this;
        }

        boolean cancelRequested() {
            return true;
        }
    }

    static final class NotifierState extends State {
        final State next;
        final Notifier notifier;
        final A attachment;

        NotifierState(final State next, final Notifier notifier, final A attachment) {
            this.next = next;
            this.notifier = notifier;
            this.attachment = attachment;
        }

        Status getStatus() {
            return Status.WAITING;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
            doNotify(future);
            next.notifyDone(future, result);
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
            doNotify(future);
            next.notifyFailed(future, exception);
        }

        void notifyCancelled(final AbstractIoFuture future) {
            doNotify(future);
            next.notifyCancelled(future);
        }

        void cancel() {
            next.cancel();
        }

        private void doNotify(final AbstractIoFuture future) {
            future.runNotifier(new NotifierRunnable(notifier, future, attachment));
        }

        boolean cancelRequested() {
            return next.cancelRequested();
        }
    }

    static final class WaiterState extends State {
        final State next;
        final Thread waiter;

        WaiterState(final State next, final Thread waiter) {
            this.next = next;
            this.waiter = waiter;
        }

        Status getStatus() {
            return Status.WAITING;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
            LockSupport.unpark(waiter);
            next.notifyDone(future, result);
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
            LockSupport.unpark(waiter);
            next.notifyFailed(future, exception);
        }

        void notifyCancelled(final AbstractIoFuture future) {
            LockSupport.unpark(waiter);
            next.notifyCancelled(future);
        }

        void cancel() {
            next.cancel();
        }

        boolean cancelRequested() {
            return next.cancelRequested();
        }
    }

    static final class CancellableState extends State {
        final State next;
        final Cancellable cancellable;

        CancellableState(final State next, final Cancellable cancellable) {
            this.next = next;
            this.cancellable = cancellable;
        }

        Status getStatus() {
            return Status.WAITING;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
            next.notifyDone(future, result);
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
            next.notifyFailed(future, exception);
        }

        void notifyCancelled(final AbstractIoFuture future) {
            next.notifyCancelled(future);
        }

        void cancel() {
            try {
                cancellable.cancel();
            } catch (Throwable ignored) {}
            next.cancel();
        }

        boolean cancelRequested() {
            return next.cancelRequested();
        }
    }

    static final class CancelRequestedState extends State {
        final State next;

        CancelRequestedState(final State next) {
            this.next = next;
        }

        Status getStatus() {
            return Status.WAITING;
        }

        void notifyDone(final AbstractIoFuture future, final T result) {
            next.notifyDone(future, result);
        }

        void notifyFailed(final AbstractIoFuture future, final IOException exception) {
            next.notifyFailed(future, exception);
        }

        void notifyCancelled(final AbstractIoFuture future) {
            next.notifyCancelled(future);
        }

        void cancel() {
            // terminate
        }

        boolean cancelRequested() {
            return true;
        }
    }

    /**
     * Construct a new instance.
     */
    protected AbstractIoFuture() {
    }

    /**
     * {@inheritDoc}
     */
    public Status getStatus() {
        return getState().getStatus();
    }

    private State getState() {
        return stateRef.get();
    }

    private boolean compareAndSetState(State expect, State update) {
        return stateRef.compareAndSet(expect, update);
    }

    /**
     * {@inheritDoc}
     */
    public Status await() {
        final Thread thread = Thread.currentThread();
        State state;
        for (;;) {
            state = getState();
            if (state.getStatus() != Status.WAITING) {
                return state.getStatus();
            }
            Xnio.checkBlockingAllowed();
            State withWaiter = state.withWaiter(thread);
            if (compareAndSetState(state, withWaiter)) {
                boolean intr = Thread.interrupted();
                try {
                    do {
                        LockSupport.park(this);
                        if (Thread.interrupted()) intr = true;
                        state = getState();
                    } while (state.getStatus() == Status.WAITING);
                    return state.getStatus();
                } finally {
                    if (intr) thread.interrupt();
                }
            }
            // retry
        }
    }

    /**
     * {@inheritDoc}
     */
    public Status await(long time, final TimeUnit timeUnit) {
        if (time < 0L) {
            time = 0L;
        }
        long duration = timeUnit.toNanos(time);
        long now = System.nanoTime();
        long tick;
        final Thread thread = Thread.currentThread();
        State state;
        for (;;) {
            state = getState();
            if (state.getStatus() != Status.WAITING || duration == 0L) {
                return state.getStatus();
            }
            Xnio.checkBlockingAllowed();
            State withWaiter = state.withWaiter(thread);
            if (compareAndSetState(state, withWaiter)) {
                boolean intr = Thread.interrupted();
                try {
                    do {
                        LockSupport.parkNanos(this, duration);
                        if (Thread.interrupted()) intr = true;
                        state = getState();
                        duration -= (tick = System.nanoTime()) - now;
                        now = tick;
                    } while (state.getStatus() == Status.WAITING && duration > 0L);
                    return state.getStatus();
                } finally {
                    if (intr) thread.interrupt();
                }
            }
            // retry
        }
    }

    /**
     * {@inheritDoc}
     */
    public Status awaitInterruptibly() throws InterruptedException {
        final Thread thread = Thread.currentThread();
        State state;
        for (;;) {
            state = getState();
            if (state.getStatus() != Status.WAITING) {
                return state.getStatus();
            }
            Xnio.checkBlockingAllowed();
            if (Thread.interrupted()) throw new InterruptedException();
            State withWaiter = state.withWaiter(thread);
            if (compareAndSetState(state, withWaiter)) {
                do {
                    LockSupport.park(this);
                    if (Thread.interrupted()) throw new InterruptedException();
                    state = getState();
                } while (state.getStatus() == Status.WAITING);
                return state.getStatus();
            }
            // retry
        }
    }

    /**
     * {@inheritDoc}
     */
    public Status awaitInterruptibly(long time, final TimeUnit timeUnit) throws InterruptedException {
        if (time < 0L) {
            time = 0L;
        }
        long duration = timeUnit.toNanos(time);
        long now = System.nanoTime();
        long tick;
        final Thread thread = Thread.currentThread();
        State state;
        for (;;) {
            state = getState();
            if (state.getStatus() != Status.WAITING || duration == 0L) {
                return state.getStatus();
            }
            Xnio.checkBlockingAllowed();
            if (Thread.interrupted()) throw new InterruptedException();
            State withWaiter = state.withWaiter(thread);
            if (compareAndSetState(state, withWaiter)) {
                do {
                    LockSupport.parkNanos(this, duration);
                    if (Thread.interrupted()) throw new InterruptedException();
                    state = getState();
                    duration -= (tick = System.nanoTime()) - now;
                    now = tick;
                } while (state.getStatus() == Status.WAITING && duration > 0L);
                return state.getStatus();
            }
            // retry
        }
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings({"unchecked"})
    public T get() throws IOException, CancellationException {
        switch (await()) {
            case DONE: return getState().getResult();
            case FAILED: throw getState().getException();
            case CANCELLED: throw futureMsg.opCancelled();
            default: throw new IllegalStateException();
        }
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings({"unchecked"})
    public T getInterruptibly() throws IOException, InterruptedException, CancellationException {
        switch (awaitInterruptibly()) {
            case DONE: return getState().getResult();
            case FAILED: throw getState().getException();
            case CANCELLED: throw futureMsg.opCancelled();
            default: throw new IllegalStateException();
        }
    }

    /**
     * {@inheritDoc}
     */
    public IOException getException() throws IllegalStateException {
        return getState().getException();
    }

    /**
     * {@inheritDoc}
     */
    public  IoFuture addNotifier(final Notifier notifier, final A attachment) {
        State oldState, newState;
        do {
            oldState = getState();
            newState = oldState.withNotifier(getNotifierExecutor(), this, notifier, attachment);
        } while (! compareAndSetState(oldState, newState));
        return this;
    }

    /**
     * Set the exception for this operation.  Any threads blocking on this instance will be unblocked.
     *
     * @param exception the exception to set
     * @return {@code false} if the operation was already completed, {@code true} otherwise
     */
    protected boolean setException(IOException exception) {
        State oldState;
        oldState = getState();
        if (oldState.getStatus() != Status.WAITING) {
            return false;
        } else {
            State newState = new FailedState(exception);
            while (! compareAndSetState(oldState, newState)) {
                oldState = getState();
                if (oldState.getStatus() != Status.WAITING) {
                    return false;
                }
            }
        }
        oldState.notifyFailed(this, exception);
        return true;
    }

    /**
     * Set the result for this operation.  Any threads blocking on this instance will be unblocked.
     *
     * @param result the result to set
     * @return {@code false} if the operation was already completed, {@code true} otherwise
     */
    protected boolean setResult(T result) {
        State oldState;
        oldState = getState();
        if (oldState.getStatus() != Status.WAITING) {
            return false;
        } else {
            State newState = new CompleteState<>(result);
            while (! compareAndSetState(oldState, newState)) {
                oldState = getState();
                if (oldState.getStatus() != Status.WAITING) {
                    return false;
                }
            }
        }
        oldState.notifyDone(this, result);
        return true;
    }

    /**
     * Acknowledge the cancellation of this operation.
     *
     * @return {@code false} if the operation was already completed, {@code true} otherwise
     */
    protected boolean setCancelled() {
        State oldState;
        oldState = getState();
        if (oldState.getStatus() != Status.WAITING) {
            return false;
        } else {
            @SuppressWarnings("unchecked")
            State newState = (State) ST_CANCELLED;
            while (! compareAndSetState(oldState, newState)) {
                oldState = getState();
                if (oldState.getStatus() != Status.WAITING) {
                    return false;
                }
            }
        }
        oldState.notifyCancelled(this);
        return true;
    }

    /**
     * Cancel an operation.  The actual cancel may be synchronous or asynchronous.  Implementers will use this method
     * to initiate the cancel; use the {@link #setCancelled()} method to indicate that the cancel was successful.  The
     * default implementation calls any registered cancel handlers.
     *
     * @return this {@code IoFuture} instance
     */
    public IoFuture cancel() {
        State state;
        do {
            state = getState();
            if (state.getStatus() != Status.WAITING || state.cancelRequested()) return this;
        } while (! compareAndSetState(state, new CancelRequestedState(state)));
        state.cancel();
        return this;
    }

    /**
     * Add a cancellation handler.  The argument will be cancelled whenever this {@code IoFuture} is cancelled.  If
     * the {@code IoFuture} is already cancelled when this method is called, the handler will be called directly.
     *
     * @param cancellable the cancel handler
     */
    protected void addCancelHandler(final Cancellable cancellable) {
        State oldState, newState;
        do {
            oldState = getState();
            if (oldState.getStatus() != Status.WAITING || oldState.cancelRequested()) {
                try {
                    cancellable.cancel();
                } catch (Throwable ignored) {
                }
                return;
            }
            newState = oldState.withCancelHandler(cancellable);
            if (oldState == newState) return;
        } while (! compareAndSetState(oldState, newState));
    }

    /**
     * Run a notifier.  Implementors will run the notifier, preferably in another thread.  The default implementation
     * runs the notifier using the {@code Executor} retrieved via {@link #getNotifierExecutor()}.
     *
     * @param runnable the runnable task
     */
    protected void runNotifier(final Runnable runnable) {
        getNotifierExecutor().execute(runnable);
    }

    /**
     * Get the executor used to run asynchronous notifiers.  By default, this implementation simply returns the direct
     * executor.
     *
     * @return the executor to use
     */
    protected Executor getNotifierExecutor() {
        return IoUtils.directExecutor();
    }

    static class NotifierRunnable implements Runnable {

        private final Notifier notifier;
        private final IoFuture future;
        private final A attachment;

        NotifierRunnable(final Notifier notifier, final IoFuture future, final A attachment) {
            this.notifier = notifier;
            this.future = future;
            this.attachment = attachment;
        }

        public void run() {
            try {
                notifier.notify(future, attachment);
            } catch (Throwable t) {
                futureMsg.notifierFailed(t, notifier, attachment);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy