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

net.openhft.chronicle.threads.BlockingEventLoop Maven / Gradle / Ivy

/*
 * Copyright 2016-2020 chronicle.software
 *
 *       https://chronicle.software
 *
 * 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 net.openhft.chronicle.threads;

import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.threads.EventHandler;
import net.openhft.chronicle.core.threads.EventLoop;
import net.openhft.chronicle.core.threads.InvalidEventHandlerException;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.function.Supplier;

import static net.openhft.chronicle.core.io.Closeable.closeQuietly;
import static net.openhft.chronicle.threads.Threads.*;

/**
 * Event Loop for blocking tasks.
 */
public class BlockingEventLoop extends AbstractLifecycleEventLoop implements EventLoop {

    @NotNull
    private transient final EventLoop parent;
    @NotNull
    private transient final ExecutorService service;
    private final List handlers = new CopyOnWriteArrayList<>();
    private final List runners = new CopyOnWriteArrayList<>();
    private final NamedThreadFactory threadFactory;
    private final Supplier pauserSupplier;

    public BlockingEventLoop(@NotNull final EventLoop parent,
                             @NotNull final String name,
                             @NotNull final Supplier pauser) {
        super(name);
        this.parent = parent;
        this.threadFactory = new NamedThreadFactory(name, null, null, true);
        this.service = Executors.newCachedThreadPool(threadFactory);
        this.pauserSupplier = pauser;
    }

    public BlockingEventLoop(@NotNull final String name) {
        super(name);
        this.parent = this;
        this.threadFactory = new NamedThreadFactory(name, null, null, true);
        this.service = Executors.newCachedThreadPool(threadFactory);
        this.pauserSupplier = Pauser::balanced;
    }

    /**
     * This can be called multiple times and each handler will be executed in its own thread
     *
     * @param handler to execute
     */
    @Override
    public synchronized void addHandler(@NotNull final EventHandler handler) {
        if (DEBUG_ADDING_HANDLERS)
            Jvm.startup().on(getClass(), "Adding " + handler.priority() + " " + handler + " to " + this.name);
        if (isClosed())
            throw new IllegalStateException("Event Group has been closed");
        eventLoopQuietly(parent, handler);
        this.handlers.add(handler);
        if (isStarted())
            this.startHandler(handler);
    }

    @Override
    protected synchronized void performStart() {
        handlers.forEach(this::startHandler);
    }

    private void startHandler(final EventHandler handler) {
        try {
            final Runner runner = new Runner(handler, pauserSupplier.get());
            runners.add(runner);
            service.submit(runner);

        } catch (RejectedExecutionException e) {
            if (!service.isShutdown())
                Jvm.warn().on(getClass(), e);
        }
    }

    @Override
    public void unpause() {
        runners.forEach(Runner::unpause);
        unpark(service);
    }

    @Override
    protected void performStopFromNew() {
        shutdownExecutorService();
    }

    @Override
    protected void performStopFromStarted() {
        shutdownExecutorService();
    }

    private void shutdownExecutorService() {
        /*
         * It's necessary for blocking handlers to be interrupted, so they abort what they're
         * doing and run to completion immediately.
         */
        service.shutdownNow();
        unpause();
        Threads.shutdown(service);
    }

    @Override
    public boolean isAlive() {
        return !service.isShutdown();
    }

    @Override
    protected void performClose() {
        super.performClose();
        closeQuietly(handlers);
        runners.clear();
    }

    @Override
    public String toString() {
        return "BlockingEventLoop{" +
                "name=" + name +
                '}';
    }

    @Override
    public boolean isRunningOnThread(Thread thread) {
        for (int i=0; i < runners.size(); i++) {
            if (thread == runners.get(i).thread()) {
                return true;
            }
        }
        return false;
    }

    private final class Runner implements Runnable {
        private final EventHandler handler;
        private final Pauser pauser;
        private boolean endedGracefully = false;
        private transient volatile Thread thread = null;

        public Runner(final EventHandler handler, Pauser pauser) {
            this.handler = handler;
            this.pauser = pauser;
        }

        @Override
        public void run() {
            try {
                throwExceptionIfClosed();
                thread = Thread.currentThread();
                handler.loopStarted();

                while (isStarted()) {
                    if (handler.action())
                        pauser.reset();
                    else
                        pauser.pause();
                }
                endedGracefully = true;
            } catch (InvalidEventHandlerException e) {
                // expected and logged below.
            } catch (Throwable t) {
                if (!isClosed())
                    Jvm.warn().on(handler.getClass(), asString(handler) + " threw ", t);

            } finally {
                if (Jvm.isDebugEnabled(handler.getClass()))
                    Jvm.debug().on(handler.getClass(), "handler " + asString(handler) + " done.");
                loopFinishedQuietly(handler);
                if (!endedGracefully) {
                    // remove handler for clarity when debugging
                    handlers.remove(handler);
                    closeQuietly(handler);
                }
                runners.remove(this);
            }
        }

        private String asString(final Object handler) {
            return Integer.toHexString(System.identityHashCode(handler));
        }

        public void unpause() {
            pauser.unpause();
        }

        public Thread thread() {
            return thread;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy