
com.kolibrifx.common.dispatcher.lmax.DisruptorDispatcher Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2017, KolibriFX AS. Licensed under the Apache License, version 2.0.
*/
package com.kolibrifx.common.dispatcher.lmax;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.kolibrifx.common.Disposable;
import com.kolibrifx.common.Safety;
import com.kolibrifx.common.dispatcher.Dispatcher;
import com.kolibrifx.common.dispatcher.ExecutorUtils;
import com.kolibrifx.common.dispatcher.ScheduledExecutorBuilder;
public class DisruptorDispatcher implements Dispatcher {
private static final Logger log = Logger.getLogger(DisruptorDispatcher.class);
private static class RunnableWrapper {
private Runnable runnable;
public void set(final Runnable runnable) {
this.runnable = runnable;
}
}
private static class RunnableWrapperFactory implements EventFactory {
@Override
public RunnableWrapper newInstance() {
return new RunnableWrapper();
}
}
private static class RunnableEventHandler implements EventHandler {
@Override
public void onEvent(final RunnableWrapper event, final long sequence, final boolean endOfBatch)
throws Exception {
event.runnable.run();
}
}
private final RingBuffer ringBuffer;
private final Disruptor disruptor;
private final ExecutorService dispatcherExecutor;
private final Thread dispatcherThread;
private final ScheduledExecutorService scheduler;
@SuppressWarnings("unchecked")
DisruptorDispatcher(final int ringBufferSize, final WaitStrategy waitStrategy, final String threadPrefix) {
dispatcherExecutor = ExecutorUtils.createSingleThreadedExecutor(threadPrefix);
try {
dispatcherThread = dispatcherExecutor.submit(new Callable() {
@Override
public Thread call() throws Exception {
return Thread.currentThread();
}
}).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Failed to start dispatcher thread", e);
}
scheduler =
new ScheduledExecutorBuilder(threadPrefix + "Scheduler").executeDelayedTasksAfterShutdown(false)
.build();
disruptor =
new Disruptor<>(new RunnableWrapperFactory(), ringBufferSize, dispatcherExecutor, ProducerType.MULTI,
waitStrategy);
disruptor.handleEventsWith(new RunnableEventHandler());
ringBuffer = disruptor.start();
}
@Override
public void runLater(final Runnable runnable) {
final long sequence = ringBuffer.next();
try {
final RunnableWrapper wrapper = ringBuffer.get(sequence);
wrapper.set(runnable);
} finally {
ringBuffer.publish(sequence);
}
}
@Override
public void runLater(final Runnable runnable, final long delayInMillis) {
if (delayInMillis <= 0) {
runLater(runnable);
} else {
final Runnable task = () -> runLater(runnable);
scheduler.schedule(task, delayInMillis, TimeUnit.MILLISECONDS);
}
}
@Override
public Disposable scheduleRepeating(final Runnable runnable, final long initialDelayInMillis,
final long intervalInMillis) {
final Runnable task = () -> runLater(runnable);
final ScheduledFuture> future =
scheduler.scheduleAtFixedRate(task, initialDelayInMillis, intervalInMillis, TimeUnit.MILLISECONDS);
return new Disposable() {
@Override
public void close() {
future.cancel(false);
}
};
}
@Override
public long currentTimeMillis() {
return System.currentTimeMillis();
}
private static void shutdownAndAwait(final ExecutorService executorService) {
executorService.shutdown();
try {
if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
log.error("Timed out awaiting termination of executor service " + executorService);
}
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Interrupted while awaiting termination of execturor service " + executorService, e);
}
}
@Override
public void close() {
shutdownAndAwait(scheduler);
disruptor.halt();
shutdownAndAwait(dispatcherExecutor);
}
@Override
public boolean onDispatcherThread() {
return Thread.currentThread() == dispatcherThread;
}
@Override
public void assertOnDispatcherThread() {
Safety.assertCurrentThread(dispatcherThread.getId());
}
@Override
public void assertNotOnDispatcherThread() {
Safety.assertDifferentThread(dispatcherThread.getId());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy