254 lines · 7.0 KB · Java
RawDownload
/* * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.ch; import java.nio.channels.*;import java.util.concurrent.*;import java.io.IOException; /** * A Future for a pending I/O operation. A PendingFuture allows for the * attachment of an additional arbitrary context object and a timer task. */ final class PendingFuture<V,A> implements Future<V> {    private static final CancellationException CANCELLED =        new CancellationException();     private final AsynchronousChannel channel;    private final CompletionHandler<V,? super A> handler;    private final A attachment;     // true if result (or exception) is available    private volatile boolean haveResult;    private volatile V result;    private volatile Throwable exc;     // latch for waiting (created lazily if needed)    private CountDownLatch latch;     // optional timer task that is cancelled when result becomes available    private Future<?> timeoutTask;     // optional context object    private volatile Object context;     PendingFuture(AsynchronousChannel channel,                  CompletionHandler<V,? super A> handler,                  A attachment,                  Object context)    {        this.channel = channel;        this.handler = handler;        this.attachment = attachment;        this.context = context;    }     PendingFuture(AsynchronousChannel channel,                  CompletionHandler<V,? super A> handler,                  A attachment)    {        this.channel = channel;        this.handler = handler;        this.attachment = attachment;    }     PendingFuture(AsynchronousChannel channel) {        this(channel, null, null);    }     PendingFuture(AsynchronousChannel channel, Object context) {        this(channel, null, null, context);    }     AsynchronousChannel channel() {        return channel;    }     CompletionHandler<V,? super A> handler() {        return handler;    }     A attachment() {        return attachment;    }     void setContext(Object context) {        this.context = context;    }     Object getContext() {        return context;    }     void setTimeoutTask(Future<?> task) {        synchronized (this) {            if (haveResult) {                task.cancel(false);            } else {                this.timeoutTask = task;            }        }    }     // creates latch if required; return true if caller needs to wait    private boolean prepareForWait() {        synchronized (this) {            if (haveResult) {                return false;            } else {                if (latch == null)                    latch = new CountDownLatch(1);                return true;            }        }    }     /**     * Sets the result, or a no-op if the result or exception is already set.     */    void setResult(V res) {        synchronized (this) {            if (haveResult)                return;            result = res;            haveResult = true;            if (timeoutTask != null)                timeoutTask.cancel(false);            if (latch != null)                latch.countDown();        }    }     /**     * Sets the result, or a no-op if the result or exception is already set.     */    void setFailure(Throwable x) {        if (!(x instanceof IOException) && !(x instanceof SecurityException))            x = new IOException(x);        synchronized (this) {            if (haveResult)                return;            exc = x;            haveResult = true;            if (timeoutTask != null)                timeoutTask.cancel(false);            if (latch != null)                latch.countDown();        }    }     /**     * Sets the result     */    void setResult(V res, Throwable x) {        if (x == null) {            setResult(res);        } else {            setFailure(x);        }    }     @Override    public V get() throws ExecutionException, InterruptedException {        if (!haveResult) {            boolean needToWait = prepareForWait();            if (needToWait)                latch.await();        }        if (exc != null) {            if (exc == CANCELLED)                throw new CancellationException();            throw new ExecutionException(exc);        }        return result;    }     @Override    public V get(long timeout, TimeUnit unit)        throws ExecutionException, InterruptedException, TimeoutException    {        if (!haveResult) {            boolean needToWait = prepareForWait();            if (needToWait)                if (!latch.await(timeout, unit)) throw new TimeoutException();        }        if (exc != null) {            if (exc == CANCELLED)                throw new CancellationException();            throw new ExecutionException(exc);        }        return result;    }     Throwable exception() {        return (exc != CANCELLED) ? exc : null;    }     V value() {        return result;    }     @Override    public boolean isCancelled() {        return (exc == CANCELLED);    }     @Override    public boolean isDone() {        return haveResult;    }     @Override    public boolean cancel(boolean mayInterruptIfRunning) {        synchronized (this) {            if (haveResult)                return false;    // already completed             // notify channel            if (channel() instanceof Cancellable)                ((Cancellable)channel()).onCancel(this);             // set result and cancel timer            exc = CANCELLED;            haveResult = true;            if (timeoutTask != null)                timeoutTask.cancel(false);        }         // close channel if forceful cancel        if (mayInterruptIfRunning) {            try {                channel().close();            } catch (IOException ignore) { }        }         // release waiters        if (latch != null)            latch.countDown();        return true;    }}