All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fluxtion.agrona.concurrent.AgentInvoker Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2024 Real Logic Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.fluxtion.agrona.concurrent;

import com.fluxtion.agrona.ErrorHandler;
import com.fluxtion.agrona.concurrent.status.AtomicCounter;

import java.nio.channels.ClosedByInterruptException;
import java.util.Objects;

/**
 * {@link Agent} container which does not start a thread. It instead allows the duty cycle {@link Agent#doWork()} to be
 * invoked directly. {@link #start()} should be called to allow the {@link Agent#onStart()} to fire before any calls
 * to {@link #invoke()} of the agent duty cycle.
 * 

* Exceptions which occur during the {@link Agent#doWork()} invocation will be caught and passed to the provided * {@link ErrorHandler}. *

* Note: This class is not threadsafe. */ public class AgentInvoker implements AutoCloseable { private boolean isClosed = false; private boolean isStarted = false; private boolean isRunning = false; private final AtomicCounter errorCounter; private final ErrorHandler errorHandler; private final Agent agent; /** * Create an agent and initialise it. * * @param errorHandler to be called if an {@link Throwable} is encountered * @param errorCounter to be incremented each time an exception is encountered. This may be null. * @param agent to be run in this thread. */ public AgentInvoker(final ErrorHandler errorHandler, final AtomicCounter errorCounter, final Agent agent) { Objects.requireNonNull(errorHandler, "errorHandler"); Objects.requireNonNull(agent, "agent"); this.errorHandler = errorHandler; this.errorCounter = errorCounter; this.agent = agent; } /** * Has the {@link Agent} been started? * * @return has the {@link Agent} been started? */ public boolean isStarted() { return isStarted; } /** * Has the {@link Agent} been running? * * @return has the {@link Agent} been started successfully and not closed? */ public boolean isRunning() { return isRunning; } /** * Has the {@link Agent} been closed? * * @return has the {@link Agent} been closed? */ public boolean isClosed() { return isClosed; } /** * The {@link Agent} which is contained. * * @return {@link Agent} being contained. */ public Agent agent() { return agent; } /** * Mark the invoker as started and call the {@link Agent#onStart()} method. *

* Startup logic will only be performed once. */ public void start() { try { if (!isStarted) { isStarted = true; agent.onStart(); isRunning = true; } } catch (final Throwable t) { errorHandler.onError(t); if (t instanceof Error) { throw (Error)t; } close(); } } /** * Invoke the {@link Agent#doWork()} method and return the work count. *

* If an error occurs then the {@link AtomicCounter#increment()} will be called on the errorCounter if not null * and the {@link Throwable} will be passed to the {@link ErrorHandler#onError(Throwable)} method. If the error * is an {@link AgentTerminationException} then {@link #close()} will be called after the error handler. *

* If not successfully started or after closed then this method will return without invoking the {@link Agent}. * * @return the work count for the {@link Agent#doWork()} method. */ public int invoke() { int workCount = 0; if (isRunning) { try { workCount = agent.doWork(); if (workCount <= 0 && Thread.currentThread().isInterrupted()) { isRunning = false; } } catch (final InterruptedException | ClosedByInterruptException ignore) { close(); Thread.currentThread().interrupt(); } catch (final AgentTerminationException ex) { isRunning = false; handleError(ex); close(); } catch (final Throwable t) { final boolean isInterrupted = Thread.currentThread().isInterrupted(); if (isInterrupted) { isRunning = false; } handleError(t); if (t instanceof Error) { throw (Error)t; } if (isInterrupted || Thread.currentThread().isInterrupted()) { isRunning = false; close(); } } } return workCount; } /** * Mark the invoker as closed and call the {@link Agent#onClose()} logic for clean up. *

* The cleanup logic will only be performed once. */ public final void close() { try { if (!isClosed) { isRunning = false; isClosed = true; agent.onClose(); } } catch (final Throwable t) { errorHandler.onError(t); if (t instanceof Error) { throw (Error)t; } } } private void handleError(final Throwable throwable) { if (null != errorCounter && isRunning && !errorCounter.isClosed()) { errorCounter.increment(); } errorHandler.onError(throwable); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy