Package co.paralleluniverse.actors.behaviors

Source Code of co.paralleluniverse.actors.behaviors.SupervisorActor$RestartHistory

/*
* 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.behaviors;

import co.paralleluniverse.actors.Actor;
import co.paralleluniverse.actors.ActorRef;
import co.paralleluniverse.actors.ActorUtil;
import co.paralleluniverse.actors.ExitMessage;
import co.paralleluniverse.actors.LifecycleMessage;
import co.paralleluniverse.actors.LocalActor;
import static co.paralleluniverse.actors.LocalActor.isLocal;
import co.paralleluniverse.actors.MailboxConfig;
import co.paralleluniverse.actors.ShutdownMessage;
import static co.paralleluniverse.actors.behaviors.RequestReplyHelper.reply;
import static co.paralleluniverse.actors.behaviors.RequestReplyHelper.replyError;
import co.paralleluniverse.actors.behaviors.Supervisor.ChildSpec;
import co.paralleluniverse.actors.behaviors.SupervisorImpl.AddChildMessage;
import co.paralleluniverse.actors.behaviors.SupervisorImpl.GetChildMessage;
import co.paralleluniverse.actors.behaviors.SupervisorImpl.RemoveChildMessage;
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.FiberScheduler;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.Strand;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import jsr166e.ConcurrentHashMapV8;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

/**
* An actor that supervises, and if necessary, restarts other actors.
*
* <p/>
* If an actor needs to know the identity of its siblings, it should add them to the supervisor manually. For that, it needs to know the identity
* of its supervisor. To do that, pass {@link Actor#self self()} to that actor's constructor in the {@link Initializer initializer}
* or the {@link #init() init} method. Alternatively, simply call {@link Actor#self self()}, which will return the supervisor actor, in the actor's constructor.
*
* This works because the children are constructed from specs (provided they have not been constructed by the caller) during the supervisor's run,
* so calling {@link Actor#self self()} anywhere in the construction process would return the supervisor.
*
* @author pron
*/
public class SupervisorActor extends BehaviorActor {
    private static final Logger LOG = LoggerFactory.getLogger(SupervisorActor.class);
    private final RestartStrategy restartStrategy;
    private List<ChildSpec> childSpec;
    private final List<ChildEntry> children = new ArrayList<ChildEntry>();
    private final ConcurrentMap<Object, ChildEntry> childrenById = new ConcurrentHashMapV8<Object, ChildEntry>();

    /**
     * Constructs a new supervisor with no children. Children may be added later via {@link Supervisor#addChild(Supervisor.ChildSpec) Supervisor.addChild}.
     *
     * @param strand          this actor's strand.
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param initializer     an optional delegate object that will be run upon actor initialization and termination. May be {@code null}.
     */
    public SupervisorActor(Strand strand, String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy, Initializer initializer) {
        super(name, initializer, strand, mailboxConfig);
        this.restartStrategy = restartStrategy;
        this.childSpec = null;
    }

    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param strand          this actor's strand.
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(Strand strand, String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy, List<ChildSpec> childSpec) {
        super(name, null, strand, mailboxConfig);
        this.restartStrategy = restartStrategy;
        this.childSpec = childSpec;
    }

    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param strand          this actor's strand.
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(Strand strand, String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy, ChildSpec... childSpec) {
        this(strand, name, mailboxConfig, restartStrategy, Arrays.asList(childSpec));
    }

    //<editor-fold defaultstate="collapsed" desc="Behavior boilerplate">
    /////////// Behavior boilerplate ///////////////////////////////////
    @Override
    protected Supervisor makeRef(ActorRef<Object> ref) {
        return new SupervisorImpl.Local(ref);
    }

    @Override
    public Supervisor ref() {
        return (Supervisor) super.ref();
    }

    @Override
    protected Supervisor self() {
        return ref();
    }

    @Override
    public Supervisor spawn(FiberScheduler scheduler) {
        return (Supervisor) super.spawn(scheduler);
    }

    @Override
    public Supervisor spawn() {
        return (Supervisor) super.spawn();
    }

    @Override
    public Supervisor spawnThread() {
        return (Supervisor) super.spawnThread();
    }

    public static SupervisorActor currentSupervisor() {
        return (SupervisorActor) Actor.<Object, Void>currentActor();
    }

    @Override
    public Logger log() {
        return LOG;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Constructors">
    /////////// Constructors ///////////////////////////////////
    /**
     * Constructs a new supervisor with no children. Children may be added later via {@link Supervisor#addChild(Supervisor.ChildSpec) Supervisor.addChild}.
     *
     * @param strand          this actor's strand.
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     */
    public SupervisorActor(Strand strand, String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy) {
        this(strand, name, mailboxConfig, restartStrategy, (Initializer) null);
    }

    /**
     * Constructs a new supervisor with no children. Children may be added later via {@link Supervisor#addChild(Supervisor.ChildSpec) Supervisor.addChild}.
     *
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     */
    public SupervisorActor(String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy) {
        this(null, name, mailboxConfig, restartStrategy, (Initializer) null);
    }

    /**
     * Constructs a new supervisor with no children. Children may be added later via {@link Supervisor#addChild(Supervisor.ChildSpec) Supervisor.addChild}.
     *
     * @param name            the actor name (may be {@code null}).
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     */
    public SupervisorActor(String name, RestartStrategy restartStrategy) {
        this(null, name, null, restartStrategy, (Initializer) null);
    }

    /**
     * Constructs a new supervisor with no children. Children may be added later via {@link Supervisor#addChild(Supervisor.ChildSpec) Supervisor.addChild}.
     *
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param initializer     an optional delegate object that will be run upon actor initialization and termination. May be {@code null}.
     */
    public SupervisorActor(String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy, Initializer initializer) {
        this(null, name, mailboxConfig, restartStrategy, initializer);
    }

    /**
     * Constructs a new supervisor with no children. Children may be added later via {@link Supervisor#addChild(Supervisor.ChildSpec) Supervisor.addChild}.
     *
     * @param name            the actor name (may be {@code null}).
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param initializer     an optional delegate object that will be run upon actor initialization and termination. May be {@code null}.
     */
    public SupervisorActor(String name, RestartStrategy restartStrategy, Initializer initializer) {
        this(null, name, null, restartStrategy, initializer);
    }

    /**
     * Constructs a new supervisor with no children. Children may be added later via {@link Supervisor#addChild(Supervisor.ChildSpec) Supervisor.addChild}.
     *
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     */
    public SupervisorActor(RestartStrategy restartStrategy) {
        this(null, null, null, restartStrategy, (Initializer) null);
    }

    ///
    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy, List<ChildSpec> childSpec) {
        this(null, name, mailboxConfig, restartStrategy, childSpec);
    }

    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param name            the actor name (may be {@code null}).
     * @param mailboxConfig   this actor's mailbox settings.
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(String name, MailboxConfig mailboxConfig, RestartStrategy restartStrategy, ChildSpec... childSpec) {
        this(null, name, mailboxConfig, restartStrategy, childSpec);
    }

    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param name            the actor name (may be {@code null}).
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(String name, RestartStrategy restartStrategy, List<ChildSpec> childSpec) {
        this(null, name, null, restartStrategy, childSpec);
    }

    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param name            the actor name (may be {@code null}).
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(String name, RestartStrategy restartStrategy, ChildSpec... childSpec) {
        this(null, name, null, restartStrategy, childSpec);
    }

    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(RestartStrategy restartStrategy, List<ChildSpec> childSpec) {
        this(null, null, null, restartStrategy, childSpec);
    }

    /**
     * Constructs a new supervisor with a given list of children.
     *
     * @param restartStrategy the supervisor's {@link RestartStrategy restart strategy}
     * @param childSpec       the supervisor's children
     */
    public SupervisorActor(RestartStrategy restartStrategy, ChildSpec... childSpec) {
        this(null, null, null, restartStrategy, childSpec);
    }
    //</editor-fold>

    @Override
    protected void init() throws InterruptedException, SuspendExecution {
        if (getInitializer() != null)
            getInitializer().init();
        else {
            if (childSpec != null) {
                try {
                    if (getInitializer() != null)
                        throw new IllegalStateException("Cannot provide a supervisor with both a child-spec list as well as an initializer");
                    if (!SupervisorActor.class.equals(this.getClass()))
                        throw new IllegalStateException("Cannot provide a subclassed supervisor with a child-spec list");

                    for (ChildSpec cs : childSpec)
                        addChild(cs);
                    this.childSpec = null;
                } catch (InterruptedException e) {
                    throw new AssertionError(e);
                }
            }
        }
    }

    @Override
    protected void onStart() throws InterruptedException, SuspendExecution {
        if (log().isInfoEnabled()) {
            //org.apache.logging.log4j.ThreadContext.push(this.toString());
            MDC.put("self", this.toString());
        }
        super.onStart();
    }

    @Override
    protected final void handleMessage(Object m1) throws InterruptedException, SuspendExecution {
        if (m1 instanceof RequestMessage) {
            final RequestMessage req = (RequestMessage) m1;
            try {
                if (req instanceof GetChildMessage) {
                    reply(req, getChild(((GetChildMessage) req).name));
                } else if (req instanceof AddChildMessage) {
                    reply(req, addChild(((AddChildMessage) req).spec));
                } else if (req instanceof RemoveChildMessage) {
                    final RemoveChildMessage m = (RemoveChildMessage) req;
                    reply(req, removeChild(m.name, m.terminate));
                }
            } catch (Exception e) {
                replyError(req, e);
            }
        }
    }

    @Override
    protected void onTerminate(Throwable cause) throws SuspendExecution, InterruptedException {
        super.onTerminate(cause);

        shutdownChildren();
        childrenById.clear();
        children.clear();

        if (log().isInfoEnabled()) {
            //org.apache.logging.log4j.ThreadContext.pop();
            MDC.remove("self");
        }
    }

    private ChildEntry addChild1(ChildSpec spec) {
        log().debug("Adding child {}", spec);
        ActorRef actor = null;
        if (spec.builder instanceof ActorRef) {
            actor = (ActorRef) spec.builder;
            if (findEntry(actor) != null)
                throw new SupervisorException("Supervisor " + this + " already supervises actor " + actor);
        }

        Object id = spec.getId();
        if (id == null && actor != null)
            id = actor.getName();
        if (id != null && findEntryById(id) != null)
            throw new SupervisorException("Supervisor " + this + " already supervises an actor by the name " + id);
        final ChildEntry child = new ChildEntry(spec, actor);
        children.add(child);
        if (id != null)
            childrenById.put(id, child);
        return child;
    }

    /**
     * Adds a new child actor to the supervisor. If the child has not been started, it will be started by the supervisor.
     *
     * @param spec the {@link ChildSpec child's spec}.
     * @return the actor (possibly after it has been started by the supervisor).
     * @throws InterruptedException
     */
    protected final <T extends ActorRef<M>, M> T addChild(ChildSpec spec) throws SuspendExecution, InterruptedException {
        verifyInActor();
        final ChildEntry child = addChild1(spec);

        ActorRef<?> actor = spec.builder instanceof ActorRef ? (ActorRef<?>) spec.builder : null;
        if (actor == null)
            actor = start(child);
        else
            start(child, actor);

        return (T) actor;
    }

    /**
     * Retrieves a child actor by its {@link ChildSpec#getId() id}
     *
     * @param id the child's {@link ChildSpec#getId() id} in the supervisor.
     * @return the child, if found; {@code null} if the child was not found
     */
    protected <T extends ActorRef<M>, M> T getChild(Object id) {
        verifyInActor();
        final ChildEntry child = findEntryById(id);
        if (child == null)
            return null;
        return (T) child.actor;
    }

    /**
     * Removes a child actor from the supervisor.
     *
     * @param id        the child's {@link ChildSpec#getId() id} in the supervisor.
     * @param terminate whether or not the supervisor should terminate the actor
     * @return {@code true} if the actor has been successfully removed from the supervisor; {@code false} if the child was not found.
     * @throws InterruptedException
     */
    protected final boolean removeChild(Object id, boolean terminate) throws SuspendExecution, InterruptedException {
        verifyInActor();
        final ChildEntry child = findEntryById(id);
        if (child == null) {
            log().warn("Child {} not found", id);
            return false;
        }

        log().debug("Removing child {}", child);
        if (child.actor != null) {
            unwatch(child);

            if (terminate)
                shutdownChild(child, false);
        }

        removeChild(child, null);

        return true;
    }

    private void removeChild(ChildEntry child, Iterator<ChildEntry> iter) {
        if (child.spec.getId() != null)
            childrenById.remove(child.spec.getId());
        if (iter != null)
            iter.remove();
        else
            children.remove(child);
    }

    @Override
    protected final Object handleLifecycleMessage(LifecycleMessage m) {
        boolean handled = false;
        try {
            if (m instanceof ExitMessage) {
                final ExitMessage death = (ExitMessage) m;
                if (death.getWatch() != null) {
                    final ActorRef actor = death.actor;
                    final ChildEntry child = findEntry(actor);

                    if (child != null) {
                        log().info("Detected child death: " + child + ". cause: ", death.cause);
                        if (!restartStrategy.onChildDeath(this, child, death.cause)) {
                            log().info("Supervisor {} giving up.", this);
                            shutdown();
                        }
                        handled = true;
                    }
                }
            }
        } catch (InterruptedException e) {
            getStrand().interrupt();
        }
        if (!handled)
            super.handleLifecycleMessage(m);
        return null;
    }

    private boolean tryRestart(ChildEntry child, Throwable cause, long now, Iterator<ChildEntry> it, boolean isDead) throws InterruptedException {
        verifyInActor();
        switch (child.spec.mode) {
            case TRANSIENT:
                if (isDead && cause == null) {
                    removeChild(child, it);
                    return true;
                }
            // fall through
            case PERMANENT:
                log().info("Supervisor trying to restart child {}. (cause: {})", child, cause);
                final ActorRef actor = child.actor;
                shutdownChild(child, true);
                child.restartHistory.addRestart(now);
                final int numRestarts = child.restartHistory.numRestarts(now - child.spec.unit.toMillis(child.spec.duration));
                if (log().isDebugEnabled())
                    log().debug("Child {} has been restarted {} times in the last {} {}s", child, numRestarts, child.spec.duration, child.spec.unit);
                if (numRestarts > child.spec.maxRestarts) {
                    log().info(this + ": too many restarts for child {}. Giving up.", actor);
                    return false;
                }
                start(child);
                return true;
            case TEMPORARY:
                if (!isDead)
                    shutdownChild(child, false);
                removeChild(child, it);
                return true;
            default:
                throw new AssertionError();
        }
    }

    private ActorRef<?> start(ChildEntry child) {
        final ActorRef old = child.actor;
        if (old != null && !LocalActor.isDone(old))
            throw new IllegalStateException("Actor " + child.actor + " cannot be restarted because it is not dead");

        final Actor actor = child.spec.builder.build();
        if (actor.getName() == null && child.spec.id != null)
            actor.setName(child.spec.id);

        log().info("{} starting child {}", this, actor);

        if (old != null && actor.getMonitor() == null && isLocal(old) && LocalActor.getMonitor(old) != null)
            actor.setMonitor(LocalActor.getMonitor(old));
        if (actor.getMonitor() != null)
            actor.getMonitor().addRestart();

        final Strand strand;
        if (actor.getStrand() != null)
            strand = actor.getStrand();
        else
            strand = createStrandForActor(child.actor != null && isLocal(child.actor) ? LocalActor.getStrand(child.actor) : null, actor);

        try {
            strand.start();
        } catch (IllegalThreadStateException e) {
            log().info("Child {} has already been started.", actor);
        }

        return start(child, actor.ref());
    }

    private ActorRef start(ChildEntry child, ActorRef actor) {
        child.actor = actor;
        child.watch = watch(actor);

        return actor;
    }

    private void shutdownChild(ChildEntry child, boolean beforeRestart) throws InterruptedException {
        if (child.actor != null) {
            unwatch(child);
            if (!isLocal(child.actor) || !LocalActor.isDone(child.actor)) {
                log().info("{} shutting down child {}", this, child.actor);
                ActorUtil.sendOrInterrupt(child.actor, new ShutdownMessage(this.ref()));
            }

            if (isLocal(child.actor)) {
                try {
                    joinChild(child);
                } finally {
                    if (!beforeRestart)
                        LocalActor.stopMonitor(child.actor);
                }
            }
            if (!beforeRestart)
                child.actor = null;
        }
    }

    private void shutdownChildren() throws InterruptedException {
        log().info("{} shutting down all children.", this);
        for (ChildEntry child : children) {
            if (child.actor != null) {
                unwatch(child);
                ActorUtil.sendOrInterrupt(child.actor, new ShutdownMessage(this.ref()));
            }
        }

        for (ChildEntry child : children) {
            if (child.actor != null && isLocal(child.actor)) {
                try {
                    joinChild(child);
                } finally {
                    LocalActor.stopMonitor(child.actor); // must be done after join to avoid a race with the actor
                }
            }
            child.actor = null;
        }
    }

    private boolean joinChild(ChildEntry child) throws InterruptedException {
        final ActorRef actor = child.actor;

        log().debug("Joining child {}", child);
        if (child.actor != null) {
            try {
                LocalActor.join(actor, child.spec.shutdownDeadline, TimeUnit.MILLISECONDS);
                log().debug("Child {} terminated normally", child.actor);
                return true;
            } catch (ExecutionException ex) {
                log().info("Child {} terminated with exception {}", child.actor, ex.getCause());
                return true;
            } catch (TimeoutException ex) {
                log().warn("Child {} shutdown timeout. Interrupting...", child.actor);
                // is this the best we can do?
                LocalActor.getStrand(actor).interrupt();

                try {
                    LocalActor.join(actor, child.spec.shutdownDeadline, TimeUnit.MILLISECONDS);
                    return true;
                } catch (ExecutionException e) {
                    log().info("Child {} terminated with exception {}", child.actor, ex.getCause());
                    return true;
                } catch (TimeoutException e) {
                    log().warn("Child {} could not shut down...", child.actor);

                    LocalActor.stopMonitor(child.actor);
                    LocalActor.unregister(child.actor);
                    child.actor = null;

                    return false;
                }
            }
        } else
            return true;
    }

    private void unwatch(ChildEntry child) {
        if (child.actor != null && child.watch != null) {
            unwatch(child.actor, child.watch);
            child.watch = null;
        }
    }

    private Strand createStrandForActor(Strand oldStrand, Actor actor) {
        final Strand strand;
        if (oldStrand != null)
            strand = Strand.clone(oldStrand, actor);
        else
            strand = new Fiber(actor);
        actor.setStrand(strand);
        return strand;
    }

    private ChildEntry findEntry(ActorRef actor) {
        if (actor.getName() != null) {
            ChildEntry child = findEntryById(actor.getName());
            if (child != null)
                return child;
        }
        for (ChildEntry child : children) {
            if (Objects.equals(child.actor, actor))
                return child;
        }
        return null;
    }

    private ChildEntry findEntryById(Object name) {
        return childrenById.get(name);
    }

    private long now() {
        return System.nanoTime() / 1000000;
    }

    /**
     * Specifies a supervisor's strategy in the event a child dies. Not every child death triggers the strategy. It is only triggered
     * when a {@link Supervisor.ChildMode#PERMANENT PERMANENET} child dies of any cause, or a
     * {@link Supervisor.ChildMode#TRANSIENT TRANSIENT} child dies an unnatural death (caused by an exception).
     */
    public enum RestartStrategy {
        /**
         * Kill the supervisor along with all children.
         */
        ESCALATE {
            @Override
            boolean onChildDeath(SupervisorActor supervisor, ChildEntry child, Throwable cause) throws InterruptedException {
                return false;
            }
        },
        /**
         * Restart the dead actor.
         */
        ONE_FOR_ONE {
            @Override
            boolean onChildDeath(SupervisorActor supervisor, ChildEntry child, Throwable cause) throws InterruptedException {
                return supervisor.tryRestart(child, cause, supervisor.now(), null, true);
            }
        },
        /**
         * Kill all surviving children, and restart them all.
         */
        ALL_FOR_ONE {
            @Override
            boolean onChildDeath(SupervisorActor supervisor, ChildEntry child, Throwable cause) throws InterruptedException {
                if (child.spec.mode == Supervisor.ChildMode.TEMPORARY
                        || (child.spec.mode == Supervisor.ChildMode.TRANSIENT && cause == null)) {
                    if (!supervisor.tryRestart(child, cause, supervisor.now(), null, true))
                        return false;
                } else {
                    supervisor.shutdownChildren();
                    for (Iterator<ChildEntry> it = supervisor.children.iterator(); it.hasNext();) {
                        final ChildEntry c = it.next();
                        if (!supervisor.tryRestart(c, c == child ? cause : null, supervisor.now(), it, c == child))
                            return false;
                    }
                }
                return true;
            }
        },
        /**
         * Kill all children that were added to the supervisor <i>after</i> the addition of the dead actor, and restart them all
         * (including the actor whose death triggered the strategy).
         */
        REST_FOR_ONE {
            @Override
            boolean onChildDeath(SupervisorActor supervisor, ChildEntry child, Throwable cause) throws InterruptedException {
                if (child.spec.mode == Supervisor.ChildMode.TEMPORARY
                        || (child.spec.mode == Supervisor.ChildMode.TRANSIENT && cause == null)) {
                    if (!supervisor.tryRestart(child, cause, supervisor.now(), null, true))
                        return false;
                } else {
                    boolean found = false;
                    for (Iterator<ChildEntry> it = supervisor.children.iterator(); it.hasNext();) {
                        final ChildEntry c = it.next();
                        if (c == child)
                            found = true;

                        if (found && !supervisor.tryRestart(c, c == child ? cause : null, supervisor.now(), it, c == child))
                            return false;
                    }
                }
                return true;
            }
        };

        abstract boolean onChildDeath(SupervisorActor supervisor, ChildEntry child, Throwable cause) throws InterruptedException;
    }

    private static class ChildEntry {
        final ChildSpec spec;
        final RestartHistory restartHistory;
        Object watch;
        volatile ActorRef<?> actor;

        public ChildEntry(ChildSpec info) {
            this(info, null);
        }

        public ChildEntry(ChildSpec info, ActorRef<?> actor) {
            this.spec = info;
            this.restartHistory = new RestartHistory(info.maxRestarts + 1);

            this.actor = actor;
        }

        @Override
        public String toString() {
            return "ActorEntry{" + "info=" + spec + " actor=" + actor + '}';
        }
    }

    private static class RestartHistory {
        private final long[] restarts;
        private int index;

        public RestartHistory(int windowSize) {
            this.restarts = new long[windowSize];
            this.index = 0;
        }

        public void addRestart(long now) {
            restarts[index] = now;
            index = mod(index + 1);
        }

        public int numRestarts(long since) {
            int count = 0;
            for (int i = mod(index - 1); i != index; i = mod(i - 1)) {
                if (restarts[i] < since) // || restarts[i] == 0L is implied
                    break;
                count++;
            }
            if (restarts[index] >= since) // || restarts[i] == 0L is implied
                count++;
            return count;
        }

        private int mod(int i) {
            // could be made fast by forcing restarts.length to be a power of two, but for now, we don't need this to be fast.
            if (i >= restarts.length)
                return i - restarts.length;
            if (i < 0)
                return i + restarts.length;
            return i;
        }
    }
}
TOP

Related Classes of co.paralleluniverse.actors.behaviors.SupervisorActor$RestartHistory

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.