co.paralleluniverse.fibers.FiberForkJoinScheduler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quasar-core Show documentation
Show all versions of quasar-core Show documentation
Fibers, Channels and Actors for the JVM
The newest version!
/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2016, 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.fibers;
import co.paralleluniverse.common.monitoring.ForkJoinPoolMonitor;
import co.paralleluniverse.common.monitoring.JMXForkJoinPoolMonitor;
import co.paralleluniverse.common.monitoring.MetricsForkJoinPoolMonitor;
import co.paralleluniverse.common.monitoring.MonitorType;
import co.paralleluniverse.concurrent.forkjoin.ExtendedForkJoinWorkerFactory;
import co.paralleluniverse.concurrent.forkjoin.ExtendedForkJoinWorkerThread;
import co.paralleluniverse.concurrent.forkjoin.MonitoredForkJoinPool;
import co.paralleluniverse.concurrent.forkjoin.ParkableForkJoinTask;
import co.paralleluniverse.fibers.instrument.DontInstrument;
import co.paralleluniverse.strands.Strand;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* A {@code ForkJoinPool} based scheduler for fibers.
*
* @author pron
*/
public class FiberForkJoinScheduler extends FiberScheduler {
private final ForkJoinPool fjPool;
private final FiberTimedScheduler timer;
private final Set activeThreads = Collections.newSetFromMap(new ConcurrentHashMap());
/**
* Creates a new fiber scheduler.
*
* @param name the scheuler's name. This name is used in naming the scheduler's threads.
* @param parallelism the number of threads in the pool
* @param exceptionHandler an {@link UncaughtExceptionHandler UncaughtExceptionHandler} to be used for exceptions thrown in fibers that aren't caught.
* @param monitorType the {@link MonitorType} type to use for the {@code ForkJoinPool}.
* @param detailedInfo whether detailed information about the fibers is collected by the fibers monitor.
*/
public FiberForkJoinScheduler(String name, int parallelism, UncaughtExceptionHandler exceptionHandler, MonitorType monitorType, boolean detailedInfo) {
super(name, monitorType, detailedInfo);
this.fjPool = createForkJoinPool(name, parallelism, exceptionHandler, monitorType);
this.timer = createTimer(fjPool, getMonitor());
}
/**
* Creates a new fiber scheduler using a default {@link UncaughtExceptionHandler UncaughtExceptionHandler}.
*
* @param name the scheuler's name. This name is used in naming the scheduler's threads.
* @param parallelism the number of threads in the pool
* @param monitorType the {@link MonitorType} type to use for the {@code ForkJoinPool}.
* @param detailedInfo whether detailed information about the fibers is collected by the fibers monitor.
*/
public FiberForkJoinScheduler(String name, int parallelism, MonitorType monitorType, boolean detailedInfo) {
this(name, parallelism, null, monitorType, detailedInfo);
}
/**
* Creates a new fiber scheduler using a default {@link UncaughtExceptionHandler UncaughtExceptionHandler} and no monitoring.
*
* @param name the scheuler's name. This name is used in naming the scheduler's threads.
* @param parallelism the number of threads in the pool
*/
public FiberForkJoinScheduler(String name, int parallelism) {
this(name, parallelism, null, null, false);
}
private FiberForkJoinScheduler(ForkJoinPool fjPool, FiberTimedScheduler timeService, boolean detailedInfo) {
super(fjPool instanceof MonitoredForkJoinPool ? ((MonitoredForkJoinPool) fjPool).getName() : null,
(fjPool instanceof MonitoredForkJoinPool && ((MonitoredForkJoinPool) fjPool).getMonitor() != null) ? MonitorType.JMX : MonitorType.NONE,
detailedInfo);
if (!fjPool.getAsyncMode())
throw new IllegalArgumentException("ForkJoinPool is not async");
this.fjPool = fjPool;
this.timer = timeService != null ? timeService : createTimer(fjPool, getMonitor());
}
public void shutdown() {
this.timer.shutdown();
super.shutdown();
}
private ForkJoinPool createForkJoinPool(String name, int parallelism, UncaughtExceptionHandler exceptionHandler, MonitorType monitorType) {
final MonitoredForkJoinPool pool = new MonitoredForkJoinPool(name, parallelism, new ExtendedForkJoinWorkerFactory(name) {
@Override
protected ExtendedForkJoinWorkerThread createThread(ForkJoinPool pool) {
return new FiberWorkerThread(pool);
}
}, exceptionHandler, true);
pool.setMonitor(createForkJoinPoolMonitor(name, pool, monitorType));
return pool;
}
static ForkJoinPoolMonitor createForkJoinPoolMonitor(String name, ForkJoinPool fjPool, MonitorType monitorType) {
if (monitorType == null)
return null;
switch (monitorType) {
case JMX:
return new JMXForkJoinPoolMonitor(name, fjPool);
case METRICS:
return new MetricsForkJoinPoolMonitor(name, fjPool);
case NONE:
return null;
default:
throw new RuntimeException("Unsupported monitor type: " + monitorType);
}
}
private FiberTimedScheduler createTimer(ForkJoinPool fjPool, FibersMonitor monitor) {
if (fjPool instanceof MonitoredForkJoinPool)
return new FiberTimedScheduler(this,
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("FiberTimedScheduler-" + ((MonitoredForkJoinPool) fjPool).getName()).build(),
monitor);
else
return new FiberTimedScheduler(this);
}
public ForkJoinPool getForkJoinPool() {
return fjPool;
}
@Override
public Executor getExecutor() {
return fjPool;
}
@Override
Future schedule(Fiber> fiber, Object blocker, long delay, TimeUnit unit) {
return timer.schedule(fiber, blocker, delay, unit);
}
@Override
FiberTask newFiberTask(Fiber fiber) {
return new FiberForkJoinTask(fiber, fjPool);
}
@Override
Map getRunningFibers() {
Map fibers = new HashMap<>(activeThreads.size() + 2);
for (FiberWorkerThread t : activeThreads)
fibers.put(t, getTargetFiber(t));
return fibers;
}
@Override
protected int getQueueLength() {
return fjPool.getQueuedSubmissionCount();
}
@Override
int getTimedQueueLength() {
return timer.getQueueLength();
}
@Override
protected final boolean isCurrentThreadInScheduler() {
return ForkJoinTask.getPool() == fjPool;
}
public static boolean isFiberThread(Thread t) {
return t instanceof FiberWorkerThread;
}
static Fiber getTargetFiber(Thread thread) {
final Object target = ParkableForkJoinTask.getTarget(thread);
if (target == null || !(target instanceof Fiber.DummyRunnable))
return null;
return ((Fiber.DummyRunnable) target).fiber;
}
@Override
void setCurrentFiber(Fiber target, Thread currentThread) {
if (isFiberThread(currentThread))
ParkableForkJoinTask.setTarget(currentThread, target.fiberRef);
else
Fiber.setCurrentStrand(target);
}
@Override
void setCurrentTarget(Object target, Thread currentThread) {
if (isFiberThread(currentThread))
ParkableForkJoinTask.setTarget(currentThread, target);
else
Fiber.setCurrentStrand((Strand) target);
}
@Override
Object getCurrentTarget(Thread currentThread) {
if (isFiberThread(currentThread))
return ParkableForkJoinTask.getTarget(currentThread);
else
return Fiber.getCurrentStrand();
}
void tryOnIdle() {
if (FiberForkJoinTask.isIdle())
onIdle();
}
protected void onIdle() {
}
private class FiberWorkerThread extends ExtendedForkJoinWorkerThread {
public FiberWorkerThread(ForkJoinPool pool) {
super(pool);
}
@Override
protected void onStart() {
super.onStart();
activeThreads.add(this);
}
@Override
protected void onTermination(Throwable exception) {
super.onTermination(exception);
activeThreads.remove(this);
}
}
static final class FiberForkJoinTask extends ParkableForkJoinTask implements FiberTask {
private final ForkJoinPool fjPool;
private final Fiber fiber;
public FiberForkJoinTask(Fiber fiber) {
this(fiber, null);
}
public FiberForkJoinTask(Fiber fiber, ForkJoinPool fjPool) {
this.fiber = fiber;
this.fjPool = fjPool;
}
@Override
public Fiber getFiber() {
return fiber;
}
@Override
public void submit() {
// final FibersMonitor monitor = fiber.getMonitor();
// if (monitor != null & fiber.getState() != Strand.State.STARTED)
// monitor.fiberResumed();
if (getPool() == fjPool)
fork();
else
fjPool.submit(this);
}
@Override
protected boolean exec1() {
return fiber.exec();
}
@Override
public boolean doExec() {
boolean done;
if (!(done = isDone())) {
if ((done = super.exec()))
quietlyComplete();
}
return done;
}
@Override
public boolean unpark(Object unblocker) {
return super.unpark(unblocker == FiberTask.EMERGENCY_UNBLOCKER ? ParkableForkJoinTask.EMERGENCY_UNBLOCKER : unblocker);
}
@Override
@DontInstrument
public boolean park(Object blocker, boolean exclusive) throws SuspendExecution {
try {
return super.park(blocker, exclusive);
} catch (SuspendExecution p) {
throw p;
} catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
@DontInstrument
public void yield() throws SuspendExecution {
try {
super.yield();
} catch (SuspendExecution p) {
throw p;
} catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
protected void parking(boolean yield) {
// do nothing. doPark will be called explicitely after the stack has been restored
}
@Override
public void doPark(boolean yield) {
super.doPark(yield);
}
@Override
protected void onParked(boolean yield) {
super.onParked(yield);
fiber.onParked();
}
@Override
@DontInstrument
protected void throwPark(boolean yield) throws SuspendExecution {
throw yield ? SuspendExecution.YIELD : SuspendExecution.PARK;
}
@Override
protected void onException(Throwable t) {
}
@Override
protected void onCompletion(boolean res) {
}
static boolean isIdle() {
return peekNextLocalTask() == null;
}
@Override
public V getRawResult() {
return fiber.getResult();
}
@Override
protected void setRawResult(V v) {
fiber.setResult(v);
}
@Override
public int getState() {
return super.getState();
}
@Override
public void setState(int state) {
super.setState(state);
}
@Override
public boolean tryUnpark(Object unblocker) {
return super.tryUnpark(unblocker);
}
@Override
public Object getUnparker() {
return super.getUnparker();
}
@Override
public StackTraceElement[] getUnparkStackTrace() {
return super.getUnparkStackTrace();
}
@Override
public String toString() {
return super.toString() + "(Fiber@" + fiber.getId() + ')';
}
}
}