Package co.paralleluniverse.strands.channels

Source Code of co.paralleluniverse.strands.channels.DelayedVal

/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*   or (per the licensee's choosing)
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.strands.channels;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.FiberScheduler;
import co.paralleluniverse.fibers.RuntimeExecutionException;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.fibers.Suspendable;
import co.paralleluniverse.strands.SimpleConditionSynchronizer;
import co.paralleluniverse.strands.SuspendableCallable;
import co.paralleluniverse.strands.SuspendableRunnable;
import co.paralleluniverse.strands.Timeout;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
* Represents a delayed value that can be set at most once, and when read, blocks until a value has been set.
*
* @author pron
*/
public class DelayedVal<V> implements Future<V> {
    private V value;
    private Throwable t;
    private SuspendableCallable<V> f;
    private volatile SimpleConditionSynchronizer sync = new SimpleConditionSynchronizer(this);

    /**
     * Creates a {@code DelayedVal} whose value will be the one returned by the given {@link SuspendableCallable}, which will be spawned
     * into a new fiber.
     * <p>
     * @param f The function that will compute this {@code DelayedVal}'s value in a newly spawned fiber
     */
    public DelayedVal(final SuspendableCallable<V> f) {
        this.f = f;
        if (f != null)
            new Fiber<Void>(new SuspendableRunnable() {

                @Override
                public void run() throws SuspendExecution {
                    try {
                        DelayedVal.this.set0(f.run());
                    } catch (Throwable t) {
                        DelayedVal.this.setException0(t);
                    }
                }
            }).start();
    }

    /**
     * Creates a {@code DelayedVal} whose value will be the one returned by the given {@link SuspendableCallable}, which will be spawned
     * into a new fiber, scheduled by the given {@link FiberScheduler}.
     * <p>
     * @param scheduler the scheduler in which the new fiber will be spawned.
     * @param f         The function that will compute this {@code DelayedVal}'s value in a newly spawned fiber
     */
    public DelayedVal(FiberScheduler scheduler, final SuspendableCallable<V> f) {
        this.f = f;
        if (f != null)
            new Fiber<Void>(scheduler, new SuspendableRunnable() {

                @Override
                public void run() throws SuspendExecution {
                    try {
                        DelayedVal.this.set0(f.run());
                    } catch (Throwable t) {
                        DelayedVal.this.setException0(t);
                    }
                }
            }).start();
    }

    public DelayedVal() {
        this(null);
    }

    /**
     * Sets the value. If the value has already been set (or if a function has been supplied to the constructor),
     * this method will throw an {@code IllegalStateException}. However, you should not rely on this behavior,
     * as the implementation is free to silently ignore additional attempts to set the value.
     *
     * @param value the value
     * @throws IllegalStateException if the value has already been set.
     */
    public final void set(V value) {
        if (f != null)
            throw new IllegalStateException("Cannot set a value because a function has been set");
        set0(value);
    }

    /**
     * Sets an exception that will be thrown by {@code get}, wrapped by {@link RuntimeExecutionException}.
     *
     * @param Throwable t the exception
     * @throws IllegalStateException if the value has already been set.
     */
    public final void setException(Throwable t) {
        if (f != null)
            throw new IllegalStateException("Cannot set a value because a function has been set");
        setException0(t);
    }

    private void set0(V value) {
        if (sync == null)
            throw new IllegalStateException("Value has already been set (and can only be set once)");
        this.value = value;
        final SimpleConditionSynchronizer s = sync;
        sync = null; // must be done before signal
        this.f = null;
        s.signalAll();
    }

    private void setException0(Throwable t) {
        if (sync == null)
            throw new IllegalStateException("Value has already been set (and can only be set once)");
        this.t = t;
        final SimpleConditionSynchronizer s = sync;
        sync = null; // must be done before signal
        this.f = null;
        s.signalAll();
    }

    @Override
    public boolean isDone() {
        return sync == null;
    }

    SimpleConditionSynchronizer getSync() {
        return sync;
    }

    V getValue() {
        return value;
    }

    /**
     * Returns the delayed value, blocking until it has been set.
     *
     * @return the value
     * @throws InterruptedException
     */
    @Override
    @Suspendable
    public V get() throws InterruptedException {
        try {
            final SimpleConditionSynchronizer s = sync;
            if (s != null) {
                Object token = s.register();
                try {
                    for (int i = 0; sync != null; i++)
                        s.await(i);
                } finally {
                    s.unregister(token);
                }
            }
            if (t != null)
                throw new RuntimeExecutionException(t);
            return value;
        } catch (SuspendExecution e) {
            throw new AssertionError(e);
        }
    }

    /**
     * Returns the delayed value, blocking until it has been set, but no longer than the given timeout.
     *
     * @param timeout The maximum duration to block waiting for the value to be set.
     * @param unit    The time unit of the timeout value.
     * @return the value
     * @throws TimeoutException     if the timeout expires before the value is set.
     * @throws InterruptedException
     */
    @Override
    @Suspendable
    public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
        try {
            final SimpleConditionSynchronizer s = sync;
            if (s != null) {
                Object token = s.register();
                try {
                    final long start = System.nanoTime();
                    long left = unit.toNanos(timeout);
                    final long deadline = start + left;
                    for (int i = 0; sync != null; i++) {
                        s.awaitNanos(i, left);
                        left = deadline - System.nanoTime();
                        if (left <= 0)
                            throw new TimeoutException();
                    }
                } finally {
                    s.unregister(token);
                }
            }
            if (t != null)
                throw new RuntimeExecutionException(t);
            return value;
        } catch (SuspendExecution e) {
            throw new AssertionError(e);
        }
    }

    public V get(Timeout timeout) throws InterruptedException, TimeoutException {
        return get(timeout.nanosLeft(), TimeUnit.NANOSECONDS);
    }

    /**
     * Throws {@code UnsupportedOperationException}.
     */
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isCancelled() {
        return false;
    }
}
TOP

Related Classes of co.paralleluniverse.strands.channels.DelayedVal

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.