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

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