/* * 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.actors; import co.paralleluniverse.fibers.Fiber; import co.paralleluniverse.fibers.Joinable; import co.paralleluniverse.fibers.SuspendExecution; import co.paralleluniverse.fibers.Suspendable; import co.paralleluniverse.strands.Strand; import co.paralleluniverse.strands.Stranded; import co.paralleluniverse.strands.SuspendableCallable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Responsible for running an actor in a strand, and responding to hot code swaps. * * @author pron */ class ActorRunner<V> implements SuspendableCallable<V>, Stranded, Joinable<V>, java.io.Serializable { private /*final*/ transient ActorRef<?> actorRef; private volatile Actor<?, V> actor; private Strand strand; ActorRunner(ActorRef<?> actorRef) { this.actorRef = actorRef; } @Override public V run() throws SuspendExecution, InterruptedException { if (strand == null) setStrand(Strand.currentStrand()); if (actor == null) { this.actor = (Actor<?, V>) actorRef.getImpl(); assert actor != null && actor == actorRef.getImpl(); } for (;;) { try { return actor.run0(); } catch (CodeSwap e) { Actor<?, V> newActor = ActorLoader.getReplacementFor(actor); if (newActor != actor) { newActor.setStrand0(strand); newActor.onCodeChange0(); actor.defunct(); this.actor = newActor; assert actor != null && actor == actorRef.getImpl(); } } catch (ActorAbort e) { return null; } } } Actor<?, V> getActor() { return actor; } @Override public void setStrand(Strand strand) { if (strand == this.strand) return; if (this.strand != null) throw new IllegalStateException("Strand already set to " + strand); this.strand = strand; if (actor == null) { this.actor = (Actor<?, V>) actorRef.getImpl(); assert actor != null : "actor == null"; assert actor == actorRef.getImpl() : "actor (" + actor + ") != actorRef.getImpl() (" + actorRef.getImpl() + ")"; } actor.setStrand0(strand); } @Override public Strand getStrand() { return strand; } public final boolean isStarted() { return strand != null && strand.getState().compareTo(Strand.State.STARTED) >= 0; } @Override @Suspendable public void join() throws ExecutionException, InterruptedException { strand.join(); } @Override @Suspendable public void join(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException { strand.join(timeout, unit); } @Override @Suspendable public final V get() throws InterruptedException, ExecutionException { final Strand s = strand; if (s == null) throw new IllegalStateException("Actor strand not set (not started?)"); if (s instanceof Fiber) return ((Fiber<V>) s).get(); else { s.join(); return actor.getResult(); } } @Override @Suspendable public final V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (strand instanceof Fiber) return ((Fiber<V>) strand).get(timeout, unit); else { strand.join(timeout, unit); return actor.getResult(); } } @Override public boolean isDone() { return actor.getDeathCause0() != null || strand.isTerminated(); } private Object readResolve() { this.actorRef = actor.ref; return this; } }