package de.renew.engine.simulator;

import de.renew.engine.thread.SimulationThreadPool;
import de.renew.util.RunQueue;
import de.renew.util.Semaphor;


/**
 * The event queue is a static service for running jobs
 * asynchronously in a dedicated thread. Formalisms are encouraged
 * to use this queue for delivering events. If they do, simulators
 * may use the method {@link #awaitEmptyQueue()} in this class to
 * avoid a listener overrun in the case of overly fast simulations.
 */
public class SimulatorEventQueue {
    /**
     * The <code>queue</code> is a global object that dispatches
     * events to those listener that do not want to be notified
     * synchronously.
     */
    private static RunQueue _queue = new RunQueue();

    /**
     * This class is completely static. No instances.
     */
    private SimulatorEventQueue() {}

    /**
     * Initialize the event queue. This method must be called
     * once before the first use of the event queue.
     */
    public static void initialize() {
        _queue = new RunQueue();
        SimulationThreadPool.getCurrent().execute(_queue);
    }

    /**
     * Delay the execution of the calling thread until
     * all currently pending messages are delivered.
     * New messages may be concurrently inserted into the
     * queue, though. They will not affect the time of
     * return.
     */
    public static void awaitEmptyQueue() {
        final Semaphor semaphore = new Semaphor();
        try {
            _queue.add(semaphore::V);
        } finally {
            semaphore.P();
        }
    }

    /**
     * Enqueue a runnable to be executed asynchronously
     * in the event queue thread.
     *
     * @param runnable the runnable to be executed.
     */
    public static void enqueue(Runnable runnable) {
        _queue.add(runnable);
    }
}