org.jruby.ext.fiber.ThreadFiber Maven / Gradle / Ivy
package org.jruby.ext.fiber;
import java.util.concurrent.Exchanger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyLocalJumpError.Reason;
import org.jruby.anno.JRubyClass;
import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@JRubyClass(name = "Fiber")
public class ThreadFiber extends Fiber {
private final Exchanger exchanger = new Exchanger();
private volatile ThreadFiberState state = ThreadFiberState.NOT_STARTED;
public ThreadFiber(Ruby runtime, RubyClass type) {
super(runtime, type);
}
protected void initFiber(ThreadContext context) {
final Ruby runtime = context.runtime;
Runnable runnable = new Runnable() {
public void run() {
// initialize and yield back to launcher
ThreadContext context = runtime.getCurrentContext();
context.setFiber(ThreadFiber.this);
IRubyObject result = yield(context, context.nil);
try {
// first resume, dive into the block
result = block.yieldArray(context, result, null, null);
} catch (JumpException.RetryJump rtry) {
// FIXME: technically this should happen before the block is executed
parent.raise(new IRubyObject[]{runtime.newSyntaxError("Invalid retry").getException()}, Block.NULL_BLOCK);
} catch (JumpException.BreakJump brk) {
parent.raise(new IRubyObject[]{runtime.newLocalJumpError(Reason.BREAK, runtime.getNil(), "break from proc-closure").getException()}, Block.NULL_BLOCK);
} catch (JumpException.ReturnJump ret) {
parent.raise(new IRubyObject[]{runtime.newLocalJumpError(Reason.RETURN, runtime.getNil(), "unexpected return").getException()}, Block.NULL_BLOCK);
} catch (RaiseException re) {
// re-raise exception in parent thread
parent.raise(new IRubyObject[]{re.getException()}, Block.NULL_BLOCK);
} finally {
state = ThreadFiberState.FINISHED;
try {
// ensure we do a final exchange to release any waiters
exchanger.exchange(result);
} catch (InterruptedException e) {
// ignore
}
}
}
};
// submit job and wait to be resumed
context.runtime.getExecutor().execute(runnable);
try {
exchanger.exchange(context.nil);
} catch (InterruptedException ie) {
throw runtime.newConcurrencyError("interrupted while waiting for fiber to start");
}
}
protected IRubyObject resumeOrTransfer(ThreadContext context, IRubyObject arg, boolean transfer) {
try {
switch (state) {
case NOT_STARTED:
if (isRoot()) {
state = ThreadFiberState.RUNNING;
return arg;
} else if (context.getThread() != parent) {
throw context.runtime.newFiberError("resuming fiber from different thread");
}
throw context.runtime.newRuntimeError("BUG: resume before fiber is started");
case YIELDED:
if (!transfer && transferredTo != null) {
throw context.getRuntime().newFiberError("double resume");
}
// update transfer fibers
if (transfer) {
transferredFrom = (ThreadFiber)context.getFiber();
transferredFrom.transferredTo = this;
}
// transfer to fiber
exchanger.exchange(arg);
arg = exchanger.exchange(context.nil);
// back from fiber, poll events
context.pollThreadEvents();
// complete transfer
if (transfer) {
if (!transferredFrom.isRoot()) {
arg = transferredFrom.yield(context, arg);
}
transferredFrom.transferredTo = null;
transferredFrom = null;
}
// return new result
return arg;
case RUNNING:
if (transfer && context.getFiber() == this) {
return arg;
}
throw context.getRuntime().newFiberError("double resume");
case FINISHED:
throw context.getRuntime().newFiberError("dead fiber called");
default:
throw context.getRuntime().newFiberError("fiber in an unknown state");
}
} catch (OutOfMemoryError oome) {
if (oome.getMessage().equals("unable to create new native thread")) {
throw context.runtime.newThreadError("too many threads, can't create a new Fiber");
}
throw oome;
} catch (InterruptedException ie) {
throw context.runtime.newConcurrencyError("interrupted waiting for fiber");
}
}
public IRubyObject yield(ThreadContext context, IRubyObject res) {
try {
state = ThreadFiberState.YIELDED;
exchanger.exchange(res);
res = exchanger.exchange(context.nil);
// back into fiber
context.pollThreadEvents();
state = ThreadFiberState.RUNNING;
return res;
} catch (InterruptedException ie) {
throw context.runtime.newConcurrencyError("interrupted while waiting for fiber to start");
}
}
public boolean isAlive() {
return state != ThreadFiberState.FINISHED;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy