package de.renew.util;

import java.util.Hashtable;


// I attach one thread to another until the other thread
// requires me to detach it. The attachment call must precede
// the detach call.

/**
 * Attaches one thread to another thread until the other thread requires a detachment. Attachment call
 * must precede the detachment call.
 */
public class Detacher {
    private static final Hashtable<PoolThread, Semaphor> ATTACHED_THREADS = new Hashtable<>();

    /**
     * Execute the runnable either in this thread
     * or in a separate thread. This method returns after
     * the <code>detach</code> method is called from the runnable,
     * if the runnable is started in a new thread. This method returns
     * in any case after the runnable completes its run.
     *
     * @see #detach()
     *
     * @param runnable the runnable to be executed
     * @param wantNewThread true, if execution should proceed in
     *   a separate thread, but initially attached
     */
    public static void possiblyStartAttached(Runnable runnable, boolean wantNewThread) {
        if (wantNewThread) {
            startAttached(runnable);
        } else {
            runnable.run();
        }
    }

    /**
     * Execute the runnable in a separate thread. This method returns after
     * the <code>detach</code> method is called from the runnable or
     * after the runnable completes its run.
     *
     * @see #detach()
     *
     * @param runnable the runnable to be executed
     */
    public static void startAttached(Runnable runnable) {
        PoolThread thread = ThreadPool.get();
        Semaphor sem = new Semaphor();
        synchronized (ATTACHED_THREADS) {
            ATTACHED_THREADS.put(thread, sem);
        }
        thread.executeOrDiscard(new DetachingRunnable(runnable));
        sem.P();
    }

    /**
     * Detach the current thread. Multiple calls to this
     * method have no effect.
     */
    public static void detach() {
        Thread thread = Thread.currentThread();
        synchronized (ATTACHED_THREADS) {
            Semaphor lock = ATTACHED_THREADS.get(thread);
            if (lock != null) {
                lock.V();
                ATTACHED_THREADS.remove(thread);
            }
        }
    }
}