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

com.lmax.disruptor.BatchEventProcessor Maven / Gradle / Ivy

/*
 * Copyright 2011 LMAX Ltd.
 *
 * 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
 *
 * http://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.lmax.disruptor;

import java.util.concurrent.atomic.AtomicInteger;


/**
 * Convenience class for handling the batching semantics of consuming entries from a {@link RingBuffer}
 * and delegating the available events to an {@link EventHandler}.
 * 

* If the {@link EventHandler} also implements {@link LifecycleAware} it will be notified just after the thread * is started and just before the thread is shutdown. * * @param event implementation storing the data for sharing during exchange or parallel coordination of an event. */ public final class BatchEventProcessor implements EventProcessor { private static final int IDLE = 0; private static final int HALTED = IDLE + 1; private static final int RUNNING = HALTED + 1; private final AtomicInteger running = new AtomicInteger(IDLE); private ExceptionHandler exceptionHandler = new FatalExceptionHandler(); private final DataProvider dataProvider; private final SequenceBarrier sequenceBarrier; private final EventHandler eventHandler; private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE); private final TimeoutHandler timeoutHandler; private final BatchStartAware batchStartAware; /** * Construct a {@link EventProcessor} that will automatically track the progress by updating its sequence when * the {@link EventHandler#onEvent(Object, long, boolean)} method returns. * * @param dataProvider to which events are published. * @param sequenceBarrier on which it is waiting. * @param eventHandler is the delegate to which events are dispatched. */ public BatchEventProcessor( final DataProvider dataProvider, final SequenceBarrier sequenceBarrier, final EventHandler eventHandler) { this.dataProvider = dataProvider; this.sequenceBarrier = sequenceBarrier; this.eventHandler = eventHandler; if (eventHandler instanceof SequenceReportingEventHandler) { ((SequenceReportingEventHandler) eventHandler).setSequenceCallback(sequence); } batchStartAware = (eventHandler instanceof BatchStartAware) ? (BatchStartAware) eventHandler : null; timeoutHandler = (eventHandler instanceof TimeoutHandler) ? (TimeoutHandler) eventHandler : null; } @Override public Sequence getSequence() { return sequence; } @Override public void halt() { running.set(HALTED); sequenceBarrier.alert(); } @Override public boolean isRunning() { return running.get() != IDLE; } /** * Set a new {@link ExceptionHandler} for handling exceptions propagated out of the {@link BatchEventProcessor} * * @param exceptionHandler to replace the existing exceptionHandler. */ public void setExceptionHandler(final ExceptionHandler exceptionHandler) { if (null == exceptionHandler) { throw new NullPointerException(); } this.exceptionHandler = exceptionHandler; } /** * It is ok to have another thread rerun this method after a halt(). * * @throws IllegalStateException if this object instance is already running in a thread */ @Override public void run() { if (running.compareAndSet(IDLE, RUNNING)) { sequenceBarrier.clearAlert(); notifyStart(); try { if (running.get() == RUNNING) { processEvents(); } } finally { notifyShutdown(); running.set(IDLE); } } else { // This is a little bit of guess work. The running state could of changed to HALTED by // this point. However, Java does not have compareAndExchange which is the only way // to get it exactly correct. if (running.get() == RUNNING) { throw new IllegalStateException("Thread is already running"); } else { earlyExit(); } } } private void processEvents() { T event = null; long nextSequence = sequence.get() + 1L; while (true) { try { final long availableSequence = sequenceBarrier.waitFor(nextSequence); if (batchStartAware != null) { batchStartAware.onBatchStart(availableSequence - nextSequence + 1); } while (nextSequence <= availableSequence) { event = dataProvider.get(nextSequence); eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence); nextSequence++; } sequence.set(availableSequence); } catch (final TimeoutException e) { notifyTimeout(sequence.get()); } catch (final AlertException ex) { if (running.get() != RUNNING) { break; } } catch (final Throwable ex) { exceptionHandler.handleEventException(ex, nextSequence, event); sequence.set(nextSequence); nextSequence++; } } } private void earlyExit() { notifyStart(); notifyShutdown(); } private void notifyTimeout(final long availableSequence) { try { if (timeoutHandler != null) { timeoutHandler.onTimeout(availableSequence); } } catch (Throwable e) { exceptionHandler.handleEventException(e, availableSequence, null); } } /** * Notifies the EventHandler when this processor is starting up */ private void notifyStart() { if (eventHandler instanceof LifecycleAware) { try { ((LifecycleAware) eventHandler).onStart(); } catch (final Throwable ex) { exceptionHandler.handleOnStartException(ex); } } } /** * Notifies the EventHandler immediately prior to this processor shutting down */ private void notifyShutdown() { if (eventHandler instanceof LifecycleAware) { try { ((LifecycleAware) eventHandler).onShutdown(); } catch (final Throwable ex) { exceptionHandler.handleOnShutdownException(ex); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy