Package org.agilewiki.jactor2.core.impl.mtReactors

Source Code of org.agilewiki.jactor2.core.impl.mtReactors.ReactorMtImpl

package org.agilewiki.jactor2.core.impl.mtReactors;

import com.google.common.collect.MapMaker;
import org.agilewiki.jactor2.core.blades.BladeBase;
import org.agilewiki.jactor2.core.closeable.Closeable;
import org.agilewiki.jactor2.core.closeable.impl.CloseableImpl;
import org.agilewiki.jactor2.core.closeable.impl.CloseableImplImpl;
import org.agilewiki.jactor2.core.impl.mtPlant.PlantConfiguration;
import org.agilewiki.jactor2.core.impl.mtPlant.PlantMtImpl;
import org.agilewiki.jactor2.core.impl.mtPlant.Recovery;
import org.agilewiki.jactor2.core.impl.mtPlant.SchedulableSemaphore;
import org.agilewiki.jactor2.core.impl.mtRequests.RequestMtImpl;
import org.agilewiki.jactor2.core.impl.mtRequests.RequestSource;
import org.agilewiki.jactor2.core.plant.PlantScheduler;
import org.agilewiki.jactor2.core.plant.impl.PlantImpl;
import org.agilewiki.jactor2.core.reactors.CommonReactor;
import org.agilewiki.jactor2.core.reactors.NonBlockingReactor;
import org.agilewiki.jactor2.core.reactors.Reactor;
import org.agilewiki.jactor2.core.reactors.ReactorClosedException;
import org.agilewiki.jactor2.core.reactors.impl.ReactorImpl;
import org.agilewiki.jactor2.core.requests.ExceptionHandler;
import org.agilewiki.jactor2.core.requests.SOp;
import org.agilewiki.jactor2.core.requests.impl.RequestImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

abstract public class ReactorMtImpl extends BladeBase implements ReactorImpl,
        RequestSource {
    /**
     * A reference to the thread that is executing this reactor.
     */
    protected final AtomicReference<Thread> threadReference = new AtomicReference<Thread>();

    private Recovery recovery;

    private PlantScheduler plantScheduler;

    private final CloseableImpl closeableImpl;

    /**
     * A set of CloseableBase objects.
     * Can only be accessed via a request to the facility.
     */
    private Set<Closeable> closeables;

    private final Set<RequestImpl<?>> inProcessRequests = new HashSet<RequestImpl<?>>();

    private volatile boolean running;

    private SchedulableSemaphore timeoutSemaphore;

    private volatile double messageStartTimeMillis;

    /**
     * The ReactorImpl logger.
     */
    protected final Logger logger;

    /**
     * The inbox of this ReactorImpl.
     */
    protected Inbox inbox;

    /**
     * Holds the buffered messages until they are passed as blocks to their
     * various destinations.
     */
    protected Outbox outbox;

    /**
     * The currently active exception handler.
     */
    private ExceptionHandler<?> exceptionHandler;

    /**
     * The request or signal message being processed.
     */
    private RequestMtImpl<?> currentRequest;

    /**
     * Set when the reactor reaches end-of-life.
     * Can only be updated via a request to the reactor.
     */
    private volatile boolean shuttingDown;

    private boolean startClosing;

    /**
     * The initial size of a send buffer.
     */
    protected int initialBufferSize;

    /**
     * The initial size of the local queue.
     */
    protected int initialLocalQueueSize;

    /**
     * The parent reactor, or null.
     */
    public final NonBlockingReactor parentReactor;

    private String reason;

    /**
     * Create a ReactorMtImpl instance.
     *
     * @param _parentReactor         The parent reactor, or null.
     * @param _initialBufferSize     The initial size of a send buffer.
     * @param _initialLocalQueueSize The initial size of the local queue.
     */
    public ReactorMtImpl(final NonBlockingReactor _parentReactor,
            final int _initialBufferSize, final int _initialLocalQueueSize) {
        closeableImpl = new CloseableImplImpl(this);
        final PlantConfiguration plantConfiguration = PlantMtImpl
                .getSingleton().getPlantConfiguration();
        @SuppressWarnings("resource")
        final NonBlockingReactorMtImpl parentReactorImpl = _parentReactor == null ? null
                : (NonBlockingReactorMtImpl) _parentReactor.asReactorImpl();
        recovery = _parentReactor == null ? plantConfiguration.getRecovery()
                : parentReactorImpl.getRecovery();
        plantScheduler = _parentReactor == null ? plantConfiguration
                .getPlantScheduler() : parentReactorImpl.getPlantScheduler();
        initialBufferSize = _initialBufferSize;
        initialLocalQueueSize = _initialLocalQueueSize;
        parentReactor = _parentReactor;
        logger = LoggerFactory.getLogger(Reactor.class);
        if (_parentReactor != null) {
            _parentReactor.addCloseable(this);
        } else {
        }
    }

    /**
     * Initialize the ReactorImpl.
     *
     * @param _reactor The Reactor of this ReactorImpl.
     */
    @Override
    public void initialize(final Reactor _reactor) throws Exception {
        super._initialize(_reactor);
        inbox = createInbox(initialLocalQueueSize);
        outbox = new Outbox(initialBufferSize);
    }

    /**
     * Returns the Reactor of this ReactorImpl.
     *
     * @return The Reactor of this ReactorImpl.
     */
    @Override
    public Reactor asReactor() {
        return getReactor();
    }

    @Override
    public CloseableImpl asCloseableImpl() {
        return closeableImpl;
    }

    /**
     * Returns the atomic reference to the reactor's thread.
     *
     * @return The atomic reference to the reactor's thread.
     */
    public AtomicReference<Thread> getThreadReference() {
        return threadReference;
    }

    /**
     * Returns the parent reactor.
     *
     * @return The parent reactor, or null.
     */
    @Override
    public NonBlockingReactor getParentReactor() {
        return parentReactor;
    }

    /**
     * Returns the initial size of a send buffer.
     *
     * @return The initial size of a send buffer.
     */
    @Override
    public int getInitialBufferSize() {
        return initialBufferSize;
    }

    /**
     * Returns the initial size of the local queue.
     *
     * @return The initial size of the local queue.
     */
    @Override
    public int getInitialLocalQueueSize() {
        return initialLocalQueueSize;
    }

    /**
     * Returns true, if this ReactorImpl is actively processing messages.
     *
     * @return True, if this ReactorImpl is actively processing messages.
     */
    @Override
    public final boolean isRunning() {
        return running;
    }

    /**
     * Returns the logger.
     *
     * @return A logger.
     */
    public Logger getLogger() {
        return logger;
    }

    /**
     * Returns true when the first phase of closing has begun.
     *
     * @return True when the first phase of closing has begun.
     */
    protected final boolean startedClosing() {
        return startClosing;
    }

    public final boolean isClosing() {
        return shuttingDown;
    }

    /**
     * Removes this ReactorImpl from any closers, closes any requests that are in process,
     * closes all closeables, closes the outbox, closes the inbox, interrupts the processing
     * of the current thread and, if the interrupt is not effective, begins hung thread recovery.
     */
    @Override
    public void close() throws Exception {
        fail(null);
    }

    public String getReasonForFailure() {
        return reason;
    }

    /**
     * Close the reactor;
     *
     * @param _reason The reason why the reactor is being closed,
     *                or null if not a failure.
     */
    @Override
    public void fail(final String _reason) throws Exception {
        reason = _reason;
        closeableImpl.close();

        if (startClosing) {
            return;
        }
        startClosing = true;

        if (closeables != null) {
            final HashSet<Closeable> hs = new HashSet<Closeable>(closeables);
            Iterator<Closeable> cit = hs.iterator();
            while (cit.hasNext()) {
                final Closeable closeable = cit.next();
                try {
                    closeable.close();
                } catch (final Throwable t) {
                    if ((closeable != null) && PlantMtImpl.DEBUG) {
                        getLogger().warn(
                                "Error closing a "
                                        + closeable.getClass().getName(), t);
                    }
                }
            }
            cit = closeables.iterator();
            while (cit.hasNext()) {
                final Closeable closeable = cit.next();
                warn("still has closable: " + this + "\n" + closeable);
            }
        }

        shuttingDown = true;

        final PlantMtImpl plantImpl = PlantMtImpl.getSingleton();
        if ((plantImpl != null) && isRunning()
                && ((currentRequest == null) || !currentRequest.isComplete())) {
            timeoutSemaphore = plantImpl.schedulableSemaphore(recovery
                    .getThreadInterruptMillis(this));
            final Thread thread = getThreadReference().get();
            if (thread != null) {
                thread.interrupt();
                boolean timeout = false;
                try {
                    timeout = timeoutSemaphore.acquire();
                } catch (InterruptedException ie) {}
                currentRequest.close();
                if (timeout
                        && (isRunning() & (PlantImpl.getSingleton() != null))) {
                    try {
                        if (currentRequest == null) {
                            logger.error("hung thread");
                        } else {
                            logger.error("hung thread\n"
                                    + currentRequest.toString());
                        }
                    } catch (final Exception ex) {
                        ex.printStackTrace();
                    }
                    recovery.onHungThread(this);
                }
            }
        }

        final Iterator<RequestImpl<?>> mit = inProcessRequests.iterator();
        while (mit.hasNext()) {
            mit.next().close();
        }

        try {
            outbox.close();
        } catch (final Exception e) {
        }

        try {
            inbox.close();
        } catch (final Exception e) {
        }
    }

    /**
     * Create the appropriate type of inbox.
     *
     * @param _initialLocalQueueSize The initial number of slots in the local queue.
     * @return An inbox.
     */
    abstract protected Inbox createInbox(int _initialLocalQueueSize);

    /**
     * Returns the message currently being processed.
     *
     * @return The message currently being processed, or null.
     */
    public final RequestMtImpl<?> getCurrentRequest() {
        return currentRequest;
    }

    /**
     * Assigns the message currently being processed.
     *
     * @param _message The message currently being processed.
     */
    public final void setCurrentRequest(final RequestMtImpl<?> _message) {
        currentRequest = _message;
    }

    /**
     * Returns true when there is a message in the inbox that can be processed.
     * (This method is not thread safe and must be called on the targetReactor's thread.)
     *
     * @return True if there is a message in the inbox that can be processed.
     */
    public final boolean hasWork() {
        return inbox.hasWork();
    }

    /**
     * Returns true when a message has been passed from another thread.
     *
     * @return True when a message has been passed from another thread.
     */
    public boolean hasConcurrent() {
        return inbox.hasConcurrent();
    }

    /**
     * Returns true when the inbox is not empty.
     *
     * @return True when the inbox is not empty.
     */
    @Override
    public final boolean isInboxEmpty() {
        return inbox.isEmpty();
    }

    /**
     * Assign an exception handler.
     *
     * @param _handler The new exception handler, or null.
     * @return The old exception handler, or null.
     */
    @Override
    public final ExceptionHandler<?> setExceptionHandler(
            final ExceptionHandler<?> _handler) {
        if (!isRunning()) {
            throw new IllegalStateException(
                    "Attempt to set an exception handler on an idle targetReactor");
        }
        final ExceptionHandler<?> rv = this.exceptionHandler;
        this.exceptionHandler = _handler;
        return rv;
    }

    /**
     * Returns the current exception handler.
     *
     * @return The current exception handler, or null.
     */
    public final ExceptionHandler<?> getExceptionHandler() {
        return exceptionHandler;
    }

    /**
     * Add a message directly to the input queue of a Reactor.
     *
     * @param _message A message.
     * @param _local   True when the current thread is assigned to the targetReactor.
     */
    public void unbufferedAddMessage(final RequestMtImpl<?> _message,
            final boolean _local) {
        if (isClosing()) {
            if (!_message.isComplete()) {
                try {
                    _message.close();
                } catch (final Throwable t) {
                }
            }
            return;
        }
        inbox.offer(_local, _message);
        afterAdd();
    }

    /**
     * Adds messages directly to the queue.
     *
     * @param _messages Previously buffered messages.
     */
    public void unbufferedAddMessages(final Queue<RequestMtImpl<?>> _messages)
            throws Exception {
        if (isClosing()) {
            final Iterator<RequestMtImpl<?>> itm = _messages.iterator();
            while (itm.hasNext()) {
                final RequestMtImpl<?> message = itm.next();
                if (!message.isComplete()) {
                    try {
                        message.close();
                    } catch (final Throwable t) {
                    }
                }
            }
            return;
        }
        inbox.offer(_messages);
        afterAdd();
    }

    /**
     * Called after adding some message(s) to the inbox.
     */
    abstract protected void afterAdd();

    /**
     * Buffers a message in the sending targetReactor for sending later.
     *
     * @param _message Message to be buffered.
     * @param _target  The reactor that should eventually receive this message
     * @return True if the message was buffered.
     */
    public boolean buffer(final RequestMtImpl<?> _message,
            final ReactorMtImpl _target) {
        return outbox.buffer(_message, _target);
    }

    /**
     * Process the event/request/response message by calling its eval method.
     *
     * @param _message The message to be processed.
     */
    protected void processMessage(final RequestMtImpl<?> _message) {
        _message.eval();
        if (!_message.isComplete() && !startClosing && !_message.isOneWay()) {
            inProcessRequests.add(_message);
        }
    }

    /**
     * Called when all pending messages that can be processed have been processed.
     */
    abstract protected void notBusy() throws Exception;

    @Override
    public final void incomingResponse(final RequestImpl<?> _message,
            final ReactorImpl _responseSource) {
        final RequestMtImpl<?> message = (RequestMtImpl<?>) _message;
        try {
            @SuppressWarnings("resource")
            final ReactorMtImpl responseSource = _responseSource == null ? null
                    : (ReactorMtImpl) _responseSource;
            final boolean local = this == _responseSource;
            if (local || (_responseSource == null)
                    || !responseSource.buffer(message, this)) {
                unbufferedAddMessage(message, local);
            }
        } catch (final Throwable t) {
            logger.error("unable to add response message", t);
        }
    }

    /**
     * Signals the start of a request.
     */
    public void requestBegin(final RequestImpl<?> _requestImpl) {
        inbox.requestBegin((RequestMtImpl<?>) _requestImpl);
    }

    /**
     * Signals that a request has completed.
     *
     * @param _message The request that has completed
     */
    public void requestEnd(final RequestImpl<?> _message) {
        final RequestMtImpl<?> message = (RequestMtImpl<?>) _message;
        if (message.isForeign()) {
            inProcessRequests.remove(_message);
        }
        inbox.requestEnd(message);
    }

    /**
     * Process any pending messages that can be processed.
     */
    @Override
    public void run() {
        running = true;
        try {
            while (true) {
                if (Thread.interrupted()) {
                    throw new InterruptedException();
                }
                if (timeoutSemaphore != null) {
                    return;
                }
                RequestMtImpl<?> request = inbox.poll();
                while ((request != null) && request._isCanceled()) {
                    request = inbox.poll();
                }
                if (request == null) {
                    try {
                        if (timeoutSemaphore != null) {
                            return;
                        }
                        notBusy();
                    } catch (final InterruptedException ie) {
                        throw ie;
                    } catch (final MigrationException me) {
                        throw me;
                    } catch (final Exception e) {
                        logger.error("Exception thrown by onIdle", e);
                    }
                    if (hasWork()) {
                        continue;
                    }
                    break;
                }
                if (timeoutSemaphore != null) {
                    return;
                }
                messageStartTimeMillis = plantScheduler.currentTimeMillis();
                processMessage(request);
                messageStartTimeMillis = 0;
            }
        } catch (final InterruptedException ie) {
            if (timeoutSemaphore == null) {
                Thread.currentThread().interrupt();
            } else if (!isClosing()) {
                logger.warn("message running too long "
                        + currentRequest.toString());
            } else if (!currentRequest.isComplete()) {
                logger.warn("message interrupted on close "
                        + currentRequest.toString());
            }
        } catch (final Exception ex) {
            throw ex;
        } finally {
            messageStartTimeMillis = 0;
            running = false;
            if (timeoutSemaphore != null) {
                timeoutSemaphore.release();
            }
        }
    }

    /**
     * A noop request used for synchronizing state.
     *
     * @return null.
     */
    @Override
    public SOp<Void> nullSOp() {
        return new SOp<Void>("null", getReactor()) {
            @Override
            protected Void processSyncOperation(RequestImpl _requestImpl) throws Exception {
                return null;
            }
        };
    }

    /**
     * Check if the current message has timed out and poll any child reactors for same.
     */
    public void reactorPoll() throws Exception {
        final double currentTimeMillis = plantScheduler.currentTimeMillis();
        final double mst = messageStartTimeMillis;
        if (mst > 0) {
            if ((mst + recovery.getMessageTimeoutMillis(this)) < currentTimeMillis) {
                recovery.onMessageTimeout(this);
            }
        }
        final Iterator<Closeable> it = getCloseableSet().iterator();
        while (it.hasNext()) {
            final Closeable closeable = it.next();
            if (!(closeable instanceof ReactorMtImpl)) {
                continue;
            }
            final ReactorMtImpl reactor = (ReactorMtImpl) closeable;
            reactor.reactorPoll();
        }
    }

    /**
     * Returns the CloseableSet. Creates it if needed.
     *
     * @return The CloseableSet.
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected final Set<Closeable> getCloseableSet() {
        if (closeables == null) {
            closeables = Collections.newSetFromMap((Map) new MapMaker()
                    .concurrencyLevel(1).weakKeys().makeMap());
        }
        return closeables;
    }

    /**
     * Add a closeable to the list of closeables.
     *
     * @param _closeable A closeable to be closed when this ReactorImpl is closed.
     * @return True when the closeable was added to the list.
     */
    @Override
    public boolean addCloseable(final Closeable _closeable) {
        if (startedClosing()) {
            throw new ReactorClosedException(
                    "call to addCloseable when reactor already started closing: "
                            + reason);
        }
        if (this == _closeable) {
            return false;
        }
        if (!getCloseableSet().add(_closeable)) {
            return false;
        }
        _closeable.asCloseableImpl().addReactor(this);
        return true;
    }

    /**
     * Remove a closeable from the list of closeables.
     *
     * @param _closeable The closeable to be removed.
     * @return True when the closeable was removed.
     */
    @Override
    public boolean removeCloseable(final Closeable _closeable) {
        if (closeables == null) {
            return false;
        }
        if (!closeables.remove(_closeable)) {
            return false;
        }
        _closeable.asCloseableImpl().removeReactor(this);
        return true;
    }

    public boolean isSlow() {
        return false;
    }

    public boolean isCommonReactor() {
        return asReactor() instanceof CommonReactor;
    }

    /**
     * The Recovery object used by this ReactorImpl.
     */
    public Recovery getRecovery() {
        return recovery;
    }

    public void setRecovery(final Recovery recovery) {
        this.recovery = recovery;
    }

    /**
     * The PlantScheduler object used by this ReactorImpl.
     */
    public PlantScheduler getPlantScheduler() {
        return plantScheduler;
    }

    public void setPlantScheduler(final PlantScheduler plantScheduler) {
        this.plantScheduler = plantScheduler;
    }

    /**
     * Log a message at the WARN level.
     *
     * @param msg the message string to be logged
     */
    @Override
    public void warn(final String msg) {
        logger.warn(msg);
    }

    /**
     * Log an exception (throwable) at the WARN level with an
     * accompanying message.
     *
     * @param msg the message accompanying the exception
     * @param t the exception (throwable) to log
     */
    @Override
    public void warn(final String msg, final Throwable t) {
        logger.warn(msg, t);
    }

    /**
     * Log a message at the ERROR level.
     *
     * @param msg the message string to be logged
     */
    @Override
    public void error(final String msg) {
        logger.error(msg);
    }

    /**
     * Log an exception (throwable) at the ERROR level with an
     * accompanying message.
     *
     * @param msg the message accompanying the exception
     * @param t the exception (throwable) to log
     */
    @Override
    public void error(final String msg, final Throwable t) {
        logger.error(msg, t);
    }
}
TOP

Related Classes of org.agilewiki.jactor2.core.impl.mtReactors.ReactorMtImpl

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.