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

org.dflib.jjava.jupyter.channels.Loop Maven / Gradle / Ivy

The newest version!
package org.dflib.jjava.jupyter.channels;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.LongSupplier;
import java.util.function.ToLongFunction;
import java.util.logging.Logger;

public class Loop extends Thread {
    private final Logger logger;

    private volatile boolean running = false;
    private final LongSupplier loopBody;

    private volatile Runnable onCloseCb;
    private volatile ToLongFunction onErrorCb;
    private final Queue runNextQueue;

    public Loop(String name, long sleep, Runnable target) {
        this(name, () -> {
            target.run();
            return sleep;
        });
    }

    public Loop(String name, LongSupplier target) {
        super(name);

        this.loopBody = target;

        this.runNextQueue = new LinkedBlockingQueue<>();

        this.logger = Logger.getLogger("Loop-" + name);
    }

    public void onClose(Runnable callback) {
        if (this.onCloseCb != null) {
            Runnable oldCallback = this.onCloseCb;
            this.onCloseCb = () -> {
                oldCallback.run();
                callback.run();
            };
        } else {
            this.onCloseCb = callback;
        }
    }

    public void onError(ToLongFunction callback) {
        if (this.onErrorCb == null) {
            this.onErrorCb = callback;
            return;
        }

        // Adding a second handler will only be invoked if the
        // previous one throws (or rethrows) the incoming exception.
        // The callback is invoked with the rethrown exception.
        ToLongFunction oldCallback = this.onErrorCb;
        this.onErrorCb = t -> {
            try {
                return oldCallback.applyAsLong(t);
            } catch (Throwable tPrime) {
                return callback.applyAsLong(tPrime);
            }
        };
    }

    public void doNext(Runnable next) {
        this.runNextQueue.offer(next);
    }

    @Override
    public void run() {
        Runnable next;
        while (this.running) {
            long sleep;
            try {
                // Run the loop body
                sleep = this.loopBody.getAsLong();

                // Run all queued tasks
                while ((next = this.runNextQueue.poll()) != null)
                    next.run();
            } catch (Throwable t) {
                if (this.onErrorCb != null)
                    sleep = this.onErrorCb.applyAsLong(t);
                else
                    throw t;
            }

            if (sleep > 0) {
                try {
                    Thread.sleep(sleep);
                } catch (InterruptedException e) {
                    this.logger.info("Loop interrupted. Stopping...");
                    this.running = false;
                }
            } else if (sleep < 0) {
                this.logger.info("Loop interrupted by a negative sleep request. Stopping...");
                this.running = false;
            }
        }

        this.logger.info("Running loop shutdown callback.");

        if (this.onCloseCb != null)
            this.onCloseCb.run();
        this.onCloseCb = null;

        this.logger.info("Loop stopped.");
    }

    @Override
    public synchronized void start() {
        this.logger.info("Loop starting...");

        this.running = true;
        super.start();

        this.logger.info("Loop started.");
    }

    public void shutdown() {
        this.running = false;
        this.logger.info("Loop shutdown.");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy