co.paralleluniverse.strands.Strand Maven / Gradle / Ivy
Show all versions of quasar-core Show documentation
/*
* 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;
import co.paralleluniverse.common.util.Exceptions;
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.FiberForkJoinScheduler;
import co.paralleluniverse.fibers.FibersMonitor;
import co.paralleluniverse.fibers.NoopFibersMonitor;
import co.paralleluniverse.fibers.SuspendExecution;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.LockSupport;
/**
* A Strand is either a Thread or a Fiber
*
* @author pron
*/
public abstract class Strand {
public static Strand of(Object owner) {
if (owner instanceof Strand)
return (Strand) owner;
// if (owner instanceof Fiber)
// return (Fiber) owner;
else
return of((Thread) owner);
}
/**
* Returns a strand representing the given thread.
*/
public static Strand of(Thread thread) {
return ThreadStrand.get(thread);
}
/**
* Returns a strand representing the given fiber.
* The current implementation simply returns the fiber itself as {@code Fiber} extends {@code Fiber}.
*/
public static Strand of(Fiber fiber) {
return fiber;
}
/**
* The minimum priority that a strand can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a strand.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a strand can have.
*/
public final static int MAX_PRIORITY = 10;
/**
* A strand's running state
*/
public static enum State {
/**
* Strand created but not started
*/
NEW,
/**
* Strand started but not yet running.
*/
STARTED,
/**
* Strand is running.
*/
RUNNING,
/**
* Strand is blocked.
*/
WAITING,
/**
* Strand is blocked with a timeout
*/
TIMED_WAITING,
/**
* Strand has terminated.
*/
TERMINATED
};
/**
* Tests whether this strand is a fiber.
*
* @return {@code true} iff this strand is a fiber.
*/
public abstract boolean isFiber();
/**
* Returns the underlying object of this strand, namely a {@code Thread} or a {@code Fiber}.
*/
public abstract Object getUnderlying();
/**
* Returns the strand's name.
*
* @return The strand's name. May be {@code null}.
*/
public abstract String getName();
/**
* Sets this strand's name.
* This method may only be called before the strand is started.
*
* @param name the new name
* @return {@code this}
*/
public abstract Strand setName(String name);
/**
* Attempts to change the priority of this strand.
*
* The priority of this thread is set to the smaller of
* the specified {@code newPriority} and the maximum permitted
* priority of the strand's thread group, if the strand is a thread.
*
* The strand priority's semantics - or even if it is ignored completely -
* is entirely up to the strand's scheduler, be it the OS kernel in the case of
* a thread, or the fiber scheduler, in the case of a fiber.
*
* @param newPriority priority to set this strand to
*
* @exception IllegalArgumentException If the priority is not in the
* range {@code MIN_PRIORITY} to {@code MAX_PRIORITY}
* @see #getPriority
* @see #MAX_PRIORITY
* @see #MIN_PRIORITY
*/
public abstract Strand setPriority(int newPriority);
/**
* Returns this strand's priority.
*
* @return this strand's priority.
* @see #setPriority
*/
public abstract int getPriority();
/**
* Tests whether this strand is alive, namely it has been started but not yet terminated.
*/
public abstract boolean isAlive();
/**
* Tests whether this strand has terminated.
*/
public abstract boolean isTerminated();
/**
* Starts the strand.
*
* @return {@code this}
* @throws IllegalThreadStateException if the strand has already been started
*/
public abstract Strand start();
/**
* Awaits the termination of this strand.
* This method blocks until this strand terminates.
*
* @throws ExecutionException if this strand has terminated as a result of an uncaught exception
* (which will be the {@link Throwable#getCause() cause} of the thrown {@code ExecutionException}.
* @throws InterruptedException
*/
public abstract void join() throws ExecutionException, InterruptedException;
/**
* Awaits the termination of this strand, at most for the timeout duration specified.
* This method blocks until this strand terminates or the timeout elapses.
*
* @param timeout the maximum duration to wait for the strand to terminate in the time unit specified by {@code unit}.
* @param unit the time unit of {@code timeout}.
*
* @throws TimeoutException if this strand did not terminate by the time the timeout has elapsed.
* @throws ExecutionException if this strand has terminated as a result of an uncaught exception
* (which will be the {@link Throwable#getCause() cause} of the thrown {@code ExecutionException}.
* @throws InterruptedException
*/
public abstract void join(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException;
public abstract Object get() throws ExecutionException, InterruptedException;
public abstract Object get(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException;
public boolean isDone() {
return isTerminated();
}
/**
* Interrupts this strand.
*
* If this strand is blocked, the blocking function will throw an {@link InterruptedException}.
* Otherwise, the strand may test its interrupted status with the {@link #interrupted()} or {@link #isInterrupted()} method.
*/
public abstract void interrupt();
/**
* Tests whether this strand has been interrupted.
*
* @return {@code true} if the strand has been interrupted; {@code false} otherwise.
* @see #interrupt()
* @see #interrupted()
*/
public abstract boolean isInterrupted();
/**
* Returns an {@link InterruptedException} that was created when the {@link #interrupt()} method was called, and can be used
* to retrieve the stack trace of the strand that interrupted this strand.
* This method is only intended to assist in debugging.
* This method may return {@code null} if this information is not available. The current implementation always returns {@code null}
* if this strand is a thread.
*/
public abstract InterruptedException getInterruptStack();
/**
* Makes available the permit for this strand, if it
* was not already available. If this strand was blocked on
* {@link #park} then it will unblock. Otherwise, its next call
* to {@link #park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* strand has not been started.
*/
public abstract void unpark();
/**
* Makes available the permit for this strand, if it
* was not already available. If this strand was blocked on
* {@link #park} then it will unblock. Otherwise, its next call
* to {@link #park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* strand has not been started.
*
* @param unblocker the synchronization object responsible for this strand unparking
*/
public abstract void unpark(Object unblocker);
/**
* Returns the blocker object supplied to the most recent
* invocation of a {@link #park(java.lang.Object) park} method that has not yet unblocked, or null
* if not blocked. The value returned is just a momentary
* snapshot -- the thread may have since unblocked or blocked on a
* different blocker object.
*
* @return the blocker
*/
public abstract Object getBlocker();
/**
* Returns the strand's current running state.
*/
public abstract State getState();
/**
* Returns an array of stack trace elements representing the stack dump
* of this strand. This method will return a zero-length array if
* this strand has not started, has started but has not yet been
* scheduled to run by the system, or has terminated.
* If the returned array is of non-zero length then the first element of
* the array represents the top of the stack, which is the most recent
* method invocation in the sequence. The last element of the array
* represents the bottom of the stack, which is the least recent method
* invocation in the sequence.
*
*
* Some virtual machines may, under some circumstances, omit one
* or more stack frames from the stack trace. In the extreme case,
* a virtual machine that has no stack trace information concerning
* this strand is permitted to return a zero-length array from this
* method.
*
* @return an array of {@link StackTraceElement}s, each represents one stack frame.
*/
public abstract StackTraceElement[] getStackTrace();
/**
* Returns the strand's id.
* Id's are unique within a single JVM instance.
*/
public abstract long getId();
/**
* Returns the current strand.
* This method will return a strand representing the fiber calling this method, or the current thread if this method is not
* called within a fiber.
*
* @return A strand representing the current fiber or thread
*/
public static Strand currentStrand() {
if (FiberForkJoinScheduler.isFiberThread(Thread.currentThread()))
return Fiber.currentFiber();
Strand s = currentStrand.get();
if (s == null) {
s = ThreadStrand.get(Thread.currentThread());
currentStrand.set(s);
}
return s;
// final Fiber fiber = Fiber.currentFiber();
// if (fiber != null)
// return of(fiber);
// else
// return ThreadStrand.currStrand();
}
/**
* Tests whether this function is called within a fiber. This method might be faster than {@code Fiber.currentFiber() != null}.
*
* @return {@code true} iff the code that called this method is executing in a fiber.
*/
public static boolean isCurrentFiber() {
return Fiber.isCurrentFiber();
}
/**
* Tests whether the current strand has been interrupted. The
* interrupted status of the strand is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return {@code false} (unless the current strand were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* @return {@code true} if the current thread has been interrupted; {@code false} otherwise.
* @see #interrupt()
* @see #isInterrupted()
*/
public static boolean interrupted() {
if (isCurrentFiber())
return Fiber.interrupted();
else
return Thread.interrupted();
}
/**
* Awaits the termination of a given strand.
* This method blocks until this strand terminates.
*
* @param strand the strand to join. May be an object of type {@code Strand}, {@code Fiber} or {@code Thread}.
*
* @throws ExecutionException if this strand has terminated as a result of an uncaught exception
* (which will be the {@link Throwable#getCause() cause} of the thrown {@code ExecutionException}.
* @throws InterruptedException
*/
public static void join(Object strand) throws ExecutionException, InterruptedException {
if (strand instanceof Strand)
((Strand) strand).join();
else if (strand instanceof Thread)
((Thread) strand).join();
else
throw new IllegalArgumentException("Can't join an object of type " + strand.getClass());
}
/**
* Awaits the termination of a given strand, at most for the timeout duration specified.
* This method blocks until this strand terminates or the timeout elapses.
*
* @param strand the strand to join. May be an object of type {@code Strand}, {@code Fiber} or {@code Thread}.
* @param timeout the maximum duration to wait for the strand to terminate in the time unit specified by {@code unit}.
* @param unit the time unit of {@code timeout}.
*
* @throws TimeoutException if this strand did not terminate by the time the timeout has elapsed.
* @throws ExecutionException if this strand has terminated as a result of an uncaught exception
* (which will be the {@link Throwable#getCause() cause} of the thrown {@code ExecutionException}.
* @throws InterruptedException
*/
public static void join(Object strand, long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
if (strand instanceof Strand)
((Strand) strand).join(timeout, unit);
else if (strand instanceof Thread)
join(Strand.of(strand), timeout, unit);
else
throw new IllegalArgumentException("Can't join an object of type " + strand.getClass());
}
/**
* A hint to the scheduler that the current strand is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
*
* Yield is a heuristic attempt to improve relative progression
* between strands that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*/
public static void yield() throws SuspendExecution {
if (isCurrentFiber())
Fiber.yield();
else
Thread.yield();
}
/**
* Causes the currently executing strand to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers.
*
* @param millis the length of time to sleep in milliseconds
*
* @throws IllegalArgumentException if the value of {@code millis} is negative
* @throws InterruptedException if any strand has interrupted the current strand. The
* interrupted status of the current strand is
* cleared when this exception is thrown.
*/
public static void sleep(long millis) throws SuspendExecution, InterruptedException {
if (isCurrentFiber())
Fiber.sleep(millis);
else
Thread.sleep(millis);
}
/**
* Causes the currently executing strand to sleep (temporarily cease
* execution) for the specified number of milliseconds plus the specified
* number of nanoseconds, subject to the precision and accuracy of system
* timers and schedulers.
*
* @param millis the length of time to sleep in milliseconds
* @param nanos {@code 0-999999} additional nanoseconds to sleep
*
* @throws IllegalArgumentException if the value of {@code millis} is negative,
* or the value of {@code nanos} is not in the range {@code 0-999999}
* @throws InterruptedException if any strand has interrupted the current strand. The
* interrupted status of the current strand is
* cleared when this exception is thrown.
*/
public static void sleep(long millis, int nanos) throws SuspendExecution, InterruptedException {
if (isCurrentFiber())
Fiber.sleep(millis, nanos);
else
Thread.sleep(millis, nanos);
}
/**
* Causes the currently executing strand to sleep (temporarily cease
* execution) for the specified duration, subject to
* the precision and accuracy of system timers and schedulers.
*
* @param duration the length of time to sleep in the time unit specified by {@code unit}.
* @param unit the time unit of {@code duration}.
*
* @throws InterruptedException if any strand has interrupted the current strand. The
* interrupted status of the current strand is
* cleared when this exception is thrown.
*/
public static void sleep(long duration, TimeUnit unit) throws SuspendExecution, InterruptedException {
if (isCurrentFiber())
Fiber.sleep(duration, unit);
else
unit.sleep(duration);
}
/**
* Disables the current strand for scheduling purposes unless the
* permit is available.
*
*
* If the permit is available then it is consumed and the call returns
* immediately; otherwise
* the current strand becomes disabled for scheduling
* purposes and lies dormant until one of three things happens:
*
*
* - Some other strand invokes {@link #unpark unpark} with the
* current strand as the target; or
*
*
- Some other strand {@link #interrupt interrupts}
* the current strand; or
*
*
- The call spuriously (that is, for no reason) returns.
*
*
*
* This method does not report which of these caused the
* method to return. Callers should re-check the conditions which caused
* the strand to park in the first place. Callers may also determine,
* for example, the interrupt status of the strand upon return.
*/
public static void park() throws SuspendExecution {
if (isCurrentFiber())
Fiber.park();
else
LockSupport.park();
}
/**
* Disables the current strand for scheduling purposes unless the
* permit is available.
*
*
* If the permit is available then it is consumed and the call returns
* immediately; otherwise
* the current strand becomes disabled for scheduling
* purposes and lies dormant until one of three things happens:
*
*
* - Some other strand invokes {@link #unpark unpark} with the
* current strand as the target; or
*
*
- Some other strand {@link #interrupt interrupts}
* the current strand; or
*
*
- The call spuriously (that is, for no reason) returns.
*
*
*
* This method does not report which of these caused the
* method to return. Callers should re-check the conditions which caused
* the strand to park in the first place. Callers may also determine,
* for example, the interrupt status of the strand upon return.
*
* @param blocker the synchronization object responsible for this strand parking
*/
public static void park(Object blocker) throws SuspendExecution {
if (isCurrentFiber())
Fiber.park(blocker);
else
LockSupport.park(blocker);
}
private static boolean canTransferControl(Strand other) {
Strand current = Strand.currentStrand();
return (other.isFiber() && current.isFiber() && ((Fiber) other).getScheduler() == ((Fiber) current).getScheduler());
}
public static void parkAndUnpark(Strand other, Object blocker) throws SuspendExecution {
if (canTransferControl(other))
Fiber.parkAndUnpark((Fiber) other, blocker);
else if (!other.isFiber() && !isCurrentFiber()) {
// might be made faster on Linux if SwitchTo syscall is introduced into the kernel.
other.unpark(blocker);
LockSupport.park(blocker);
} else {
other.unpark(blocker);
park(blocker);
}
}
public static void parkAndUnpark(Strand other) throws SuspendExecution {
if (canTransferControl(other))
Fiber.parkAndUnpark((Fiber) other);
else if (!other.isFiber() && !isCurrentFiber()) {
// might be made faster on Linux if SwitchTo syscall is introduced into the kernel.
other.unpark();
LockSupport.park();
} else {
other.unpark();
park();
}
}
public static void yieldAndUnpark(Strand other, Object blocker) throws SuspendExecution {
if (canTransferControl(other))
Fiber.yieldAndUnpark((Fiber) other, blocker);
else if (!other.isFiber() && !isCurrentFiber()) {
// might be made faster on Linux if SwitchTo syscall is introduced into the kernel.
other.unpark(blocker);
//Thread.yield(); - it's a shame to yield now as we'll shortly block
} else {
other.unpark(blocker);
//yield(); - it's a shame to yield now as we'll shortly block
}
}
public static void yieldAndUnpark(Strand other) throws SuspendExecution {
if (other.isFiber() && isCurrentFiber())
Fiber.yieldAndUnpark((Fiber) other);
else if (!other.isFiber() && !isCurrentFiber()) {
// might be made faster on Linux if SwitchTo syscall is introduced into the kernel.
other.unpark();
//Thread.yield(); - it's a shame to yield now as we'll shortly block
} else {
other.unpark();
//yield(); - it's a shame to yield now as we'll shortly block
}
}
/**
* Disables the current strand for thread scheduling purposes, for up to
* the specified waiting time, unless the permit is available.
*
*
* If the permit is available then it is consumed and the call
* returns immediately; otherwise the current strand becomes disabled
* for scheduling purposes and lies dormant until one of four
* things happens:
*
*
* - Some other strand invokes {@link #unpark unpark} with the
* current strand as the target; or
*
*
- Some other strand {@link #interrupt interrupts}
* the current strand; or
*
*
- The specified waiting time elapses; or
*
*
- The call spuriously (that is, for no reason) returns.
*
*
*
* This method does not report which of these caused the
* method to return. Callers should re-check the conditions which caused
* the strand to park in the first place. Callers may also determine,
* for example, the interrupt status of the strand, or the elapsed time
* upon return.
*
* @param nanos the maximum number of nanoseconds to wait
*/
public static void parkNanos(long nanos) throws SuspendExecution {
if (isCurrentFiber())
Fiber.park(nanos, TimeUnit.NANOSECONDS);
else
LockSupport.parkNanos(nanos);
}
/**
* Disables the current strand for thread scheduling purposes, for up to
* the specified waiting time, unless the permit is available.
*
*
* If the permit is available then it is consumed and the call
* returns immediately; otherwise the current strand becomes disabled
* for scheduling purposes and lies dormant until one of four
* things happens:
*
*
* - Some other strand invokes {@link #unpark unpark} with the
* current strand as the target; or
*
*
- Some other strand {@link #interrupt interrupts}
* the current strand; or
*
*
- The specified waiting time elapses; or
*
*
- The call spuriously (that is, for no reason) returns.
*
*
*
* This method does not report which of these caused the
* method to return. Callers should re-check the conditions which caused
* the strand to park in the first place. Callers may also determine,
* for example, the interrupt status of the strand, or the elapsed time
* upon return.
*
* @param blocker the synchronization object responsible for this strand parking
* @param nanos the maximum number of nanoseconds to wait
*/
public static void parkNanos(Object blocker, long nanos) throws SuspendExecution {
if (isCurrentFiber())
Fiber.park(blocker, nanos, TimeUnit.NANOSECONDS);
else
LockSupport.parkNanos(blocker, nanos);
}
/**
* Disables the current strand for scheduling purposes, until
* the specified deadline, unless the permit is available.
*
*
* If the permit is available then it is consumed and the call
* returns immediately; otherwise the current strand becomes disabled
* for scheduling purposes and lies dormant until one of four
* things happens:
*
*
* - Some other strand invokes {@link #unpark unpark} with the
* current strand as the target; or
*
*
- Some other strand {@link #interrupt interrupts} the
* current strand; or
*
*
- The specified deadline passes; or
*
*
- The call spuriously (that is, for no reason) returns.
*
*
*
* This method does not report which of these caused the
* method to return. Callers should re-check the conditions which caused
* the strand to park in the first place. Callers may also determine,
* for example, the interrupt status of the strand, or the current time
* upon return.
*
* @param blocker the synchronization object responsible for this strand parking
* @param deadline the absolute time, in milliseconds from the Epoch, to wait until
*/
public static void parkUntil(Object blocker, long deadline) throws SuspendExecution {
if (isCurrentFiber()) {
final long delay = deadline - System.currentTimeMillis();
if (delay > 0)
Fiber.park(blocker, delay, TimeUnit.MILLISECONDS);
} else
LockSupport.parkUntil(blocker, deadline);
}
/**
* Makes available the permit for the given strand, if it
* was not already available. If the strand was blocked on
* {@code park} then it will unblock. Otherwise, its next call
* to {@code park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* strand has not been started.
*
* @param strand the strand to unpark, or {@code null}, in which case this operation has no effect
*/
public static void unpark(Strand strand) {
if (strand != null)
strand.unpark();
}
/**
* Makes available the permit for the given strand, if it
* was not already available. If the strand was blocked on
* {@code park} then it will unblock. Otherwise, its next call
* to {@code park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* strand has not been started.
*
* @param strand the strand to unpark, or {@code null}, in which case this operation has no effect
* @param unblocker the synchronization object responsible for the strand unparking
*/
public static void unpark(Strand strand, Object unblocker) {
if (strand != null)
strand.unpark(unblocker);
}
/**
* Makes available the permit for the given strand, if it
* was not already available. If the strand was blocked on
* {@code park} then it will unblock. Otherwise, its next call
* to {@code park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* strand has not been started.
*
* @param strand the strand to unpark, or {@code null}, in which case this operation has no effect
*/
public static void unpark(Thread strand) {
LockSupport.unpark(strand);
}
/**
* Prints a stack trace of the current strand to the standard error stream.
* This method is used only for debugging.
*/
@SuppressWarnings({"CallToThreadDumpStack", "CallToPrintStackTrace"})
public static void dumpStack() {
if (isCurrentFiber())
Fiber.dumpStack();
else
Thread.dumpStack();
}
/**
* Set the handler invoked when this strand abruptly terminates
* due to an uncaught exception.
*
* A strand can take full control of how it responds to uncaught
* exceptions by having its uncaught exception handler explicitly set.
*
* @param eh the object to use as this strand's uncaught exception handler.
* If {@code null} then this strand has no explicit handler.
*/
public abstract void setUncaughtExceptionHandler(UncaughtExceptionHandler eh);
/**
* Returns the handler invoked when this strand abruptly terminates
* due to an uncaught exception.
*/
public abstract UncaughtExceptionHandler getUncaughtExceptionHandler();
/**
* Tests whether two strands represent the same fiber or thread.
*
* @param strand1 May be an object of type {@code Strand}, {@code Fiber} or {@code Thread}.
* @param strand2 May be an object of type {@code Strand}, {@code Fiber} or {@code Thread}.
* @return {@code true} if the two strands represent the same fiber or the same thread; {@code false} otherwise.
*/
public static boolean equals(Object strand1, Object strand2) {
if (strand1 == strand2)
return true;
if (strand1 == null | strand2 == null)
return false;
return of(strand1).equals(of(strand2));
}
public static Strand clone(Strand strand, final SuspendableCallable> target) {
if (strand.isAlive())
throw new IllegalStateException("A strand can only be cloned after death. " + strand + " isn't dead.");
if (strand instanceof FiberStrand)
return clone((Fiber) strand.getUnderlying(), target);
if (strand instanceof Fiber)
return new Fiber((Fiber) strand, target);
else
return ThreadStrand.get(cloneThread((Thread) strand.getUnderlying(), toRunnable(target)));
}
public static Strand clone(Strand strand, final SuspendableRunnable target) {
if (strand.isAlive())
throw new IllegalStateException("A strand can only be cloned after death. " + strand + " isn't dead.");
if (strand instanceof FiberStrand)
return clone((Fiber) strand.getUnderlying(), target);
if (strand instanceof Fiber)
return new Fiber((Fiber) strand, target);
else
return ThreadStrand.get(cloneThread((Thread) strand.getUnderlying(), toRunnable(target)));
}
/**
* A utility method that converts a {@link SuspendableRunnable} to a {@link Runnable} so that it could run
* as the target of a thread.
*/
public static Runnable toRunnable(final SuspendableRunnable runnable) {
return new SuspendableRunnableRunnable(runnable);
}
/**
* A utility method that converts a {@link SuspendableCallable} to a {@link Runnable} so that it could run
* as the target of a thread. The return value of the callable is ignored.
*/
public static Runnable toRunnable(final SuspendableCallable> callable) {
return new SuspendableCallableRunnable(callable);
}
/**
* Returns the {@link SuspendableCallable} or {@link SuspendableRunnable}, wrapped by the given {@code Runnable}
* by {@code toRunnable}.
*/
public static Object unwrapSuspendable(Runnable r) {
if (r instanceof SuspendableCallableRunnable)
return ((SuspendableCallableRunnable) r).callable;
if (r instanceof SuspendableRunnableRunnable)
return ((SuspendableRunnableRunnable) r).runnable;
return null;
}
private static class SuspendableRunnableRunnable implements Runnable {
private final SuspendableRunnable runnable;
public SuspendableRunnableRunnable(SuspendableRunnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
try {
runnable.run();
} catch (SuspendExecution ex) {
throw new AssertionError(ex);
} catch (InterruptedException ex) {
} catch (Exception e) {
throw Exceptions.rethrow(e);
}
}
}
private static class SuspendableCallableRunnable implements Runnable {
private final SuspendableCallable> callable;
public SuspendableCallableRunnable(SuspendableCallable> callable) {
this.callable = callable;
}
@Override
public void run() {
try {
callable.run();
} catch (SuspendExecution ex) {
throw new AssertionError(ex);
} catch (InterruptedException ex) {
} catch (Exception e) {
throw Exceptions.rethrow(e);
}
}
}
private static Thread cloneThread(Thread thread, Runnable target) {
Thread t = new Thread(thread.getThreadGroup(), target, thread.getName());
t.setDaemon(thread.isDaemon());
return t;
}
/**
* This utility method turns a stack-trace into a human readable, multi-line string.
*
* @param trace a stack trace (such as returned from {@link #getStackTrace()}.
* @return a nice (multi-line) string representation of the stack trace.
*/
public static String toString(StackTraceElement[] trace) {
if (trace == null)
return "null";
StringBuilder sb = new StringBuilder();
for (StackTraceElement traceElement : trace)
sb.append("\tat ").append(traceElement).append('\n');
return sb.toString();
}
/**
* This utility method prints a stack-trace into a {@link java.io.PrintStream}
*
* @param trace a stack trace (such as returned from {@link #getStackTrace()}.
* @param out the {@link java.io.PrintStream} into which the stack trace will be printed.
*/
public static void printStackTrace(StackTraceElement[] trace, java.io.PrintStream out) {
if (trace == null)
out.println("No stack trace");
else {
for (StackTraceElement traceElement : trace)
out.println("\tat " + traceElement);
}
}
/**
* This utility method prints a stack-trace into a {@link java.io.PrintWriter}
*
* @param trace a stack trace (such as returned from {@link #getStackTrace()}.
* @param out the {@link java.io.PrintWriter} into which the stack trace will be printed.
*/
public static void printStackTrace(StackTraceElement[] trace, java.io.PrintWriter out) {
if (trace == null)
out.println("No stack trace");
else {
for (StackTraceElement traceElement : trace)
out.println("\tat " + traceElement);
}
}
/**
* Interface for handlers invoked when a {@code Strand} abruptly terminates due to an uncaught exception.
*
* When a fiber is about to terminate due to an uncaught exception,
* the Java Virtual Machine will query the fiber for its
* {@code UncaughtExceptionHandler} using
* {@link #getUncaughtExceptionHandler} and will invoke the handler's
* {@code uncaughtException} method, passing the fiber and the
* exception as arguments.
*
* @see #setUncaughtExceptionHandler(UncaughtExceptionHandler)
*/
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given fiber terminates due to the given uncaught exception.
*
* Any exception thrown by this method will be ignored.
*
* @param f the fiber
* @param e the exception
*/
void uncaughtException(Strand f, Throwable e);
}
protected static ThreadLocal currentStrand = new ThreadLocal();
private static final class ThreadStrand extends Strand {
private static final ConcurrentMap threadStrands = new com.google.common.collect.MapMaker().weakValues().makeMap();
static Strand get(Thread t) {
Strand s = threadStrands.get(t.getId());
if (s == null) {
s = new ThreadStrand(t);
Strand p = threadStrands.putIfAbsent(t.getId(), s);
if (p != null)
s = p;
// else {
// final Runnable target = ThreadAccess.getTarget(t);
// if (target != null && target instanceof Stranded)
// ((Stranded) target).setStrand(s);
// }
}
return s;
}
static Strand currStrand() {
return currentStrand.get();
}
private final Thread thread;
public ThreadStrand(Thread owner) {
this.thread = owner;
}
@Override
public boolean isFiber() {
return false;
}
@Override
public Thread getUnderlying() {
return thread;
}
@Override
public String getName() {
return thread.getName();
}
@Override
public Strand setName(String name) {
thread.setName(name);
return this;
}
@Override
public Strand setPriority(int newPriority) {
thread.setPriority(newPriority);
return this;
}
@Override
public int getPriority() {
return thread.getPriority();
}
@Override
public long getId() {
return thread.getId();
}
@Override
public boolean isAlive() {
return thread.isAlive();
}
@Override
public boolean isTerminated() {
return thread.getState() == Thread.State.TERMINATED;
}
@Override
public State getState() {
final Thread.State state = thread.getState();
switch (state) {
case NEW:
return State.NEW;
case RUNNABLE:
return State.STARTED;
case BLOCKED:
case WAITING:
return State.WAITING;
case TIMED_WAITING:
return State.TIMED_WAITING;
case TERMINATED:
return State.TERMINATED;
default:
throw new AssertionError("Unknown thread state: " + state);
}
}
@Override
public Strand start() {
thread.start();
return this;
}
@Override
public void join() throws InterruptedException {
thread.join();
}
@Override
public void join(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
long nanos = unit.toNanos(timeout);
long millis = TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS);
thread.join(millis, (int) (nanos - TimeUnit.MILLISECONDS.toNanos(millis)));
if (thread.isAlive())
throw new TimeoutException();
}
@Override
public Object get() throws ExecutionException, InterruptedException {
return null;
}
@Override
public Object get(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
return null;
}
@Override
public void interrupt() {
thread.interrupt();
}
@Override
public boolean isInterrupted() {
return thread.isInterrupted();
}
@Override
public InterruptedException getInterruptStack() {
return null;
}
@Override
public void unpark() {
LockSupport.unpark(thread);
}
@Override
public void unpark(Object unblocker) {
unpark();
}
@Override
public Object getBlocker() {
return LockSupport.getBlocker(thread);
}
@Override
public StackTraceElement[] getStackTrace() {
return thread.getStackTrace();
}
@Override
public void setUncaughtExceptionHandler(final UncaughtExceptionHandler uncaughtExceptionHandler) {
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
uncaughtExceptionHandler.uncaughtException(Strand.of(t), e);
}
});
}
@Override
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return thread.toString();
}
@Override
public int hashCode() {
return thread.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof ThreadStrand))
return false;
return this.thread.equals(((ThreadStrand) obj).thread);
}
}
private static class FiberStrand extends Strand {
private final Fiber fiber;
public FiberStrand(Fiber owner) {
this.fiber = owner;
}
@Override
public boolean isFiber() {
return true;
}
@Override
public Fiber getUnderlying() {
return fiber;
}
@Override
public String getName() {
return fiber.getName();
}
@Override
public Strand setName(String name) {
return fiber.setName(name);
}
/**
* @inheritDoc
*
* Note that the default fiber scheduler ignores fiber priority.
*/
@Override
public Strand setPriority(int newPriority) {
fiber.setPriority(newPriority);
return this;
}
@Override
public int getPriority() {
return fiber.getPriority();
}
@Override
public long getId() {
return fiber.getId();
}
@Override
public boolean isAlive() {
return fiber.isAlive();
}
@Override
public boolean isTerminated() {
return fiber.isTerminated();
}
@Override
public State getState() {
return fiber.getState();
}
@Override
public Strand start() {
fiber.start();
return this;
}
@Override
public void join() throws ExecutionException, InterruptedException {
fiber.join();
}
@Override
public void join(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
fiber.join(timeout, unit);
}
@Override
public Object get() throws ExecutionException, InterruptedException {
return fiber.get();
}
@Override
public Object get(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
return fiber.get(timeout, unit);
}
@Override
public void interrupt() {
fiber.interrupt();
}
@Override
public boolean isInterrupted() {
return fiber.isInterrupted();
}
@Override
public InterruptedException getInterruptStack() {
return fiber.getInterruptStack();
}
@Override
public void unpark() {
fiber.unpark();
}
@Override
public void unpark(Object unblocker) {
fiber.unpark(unblocker);
}
@Override
public Object getBlocker() {
return fiber.getBlocker();
}
@Override
public StackTraceElement[] getStackTrace() {
return fiber.getStackTrace();
}
@Override
public void setUncaughtExceptionHandler(UncaughtExceptionHandler uncaughtExceptionHandler) {
fiber.setUncaughtExceptionHandler(uncaughtExceptionHandler);
}
@Override
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return fiber.getUncaughtExceptionHandler();
}
@Override
public String toString() {
return fiber.toString();
}
@Override
public int hashCode() {
return fiber.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof FiberStrand))
return false;
return this.fiber.equals(((FiberStrand) obj).fiber);
}
}
private static final FibersMonitor NOOP_FIBERS_MONITOR = new NoopFibersMonitor();
}