//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010 huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/willow
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : willow@lists.sourceforge.net 
 */
//----------------------------END  LICENSE----------------------------

package org.huihoo.workflow.event;

import java.util.HashSet;

import org.huihoo.workflow.WorkflowComponent;

public class EventAutoShutdown implements Runnable, WorkflowComponent
{

	private static final EventAutoShutdown theInstance = new EventAutoShutdown();

	/**
	 * This lock object is used to synchronize shutdown operations. 
	 */
	private final Object mainLock = new Object();

	/** 
	 * This lock object is to secure that when a new blocker thread is 
	 * started it will be the first who acquire the main lock after 
	 * the thread that created the new blocker released the main lock
	 * by calling lock.wait() to wait for the blocker to start. 
	 */
	private final Object activationLock = new Object();

	/** 
	 * This set keeps references to all the event dispatch threads that
	 * are busy at this moment, i.e. those that are not waiting for a
	 * new event to appear in their event queue.       
	 * Access is synchronized on the main lock object. 
	 */
	private final HashSet busyThreadSet = new HashSet(7);

	/** 
	 * References the alive non-daemon thread that is currently used 
	 * for keeping WILLOW from exiting. 
	 */
	private Thread blockerThread = null;

	/**
	 * We need this flag to secure that WILLOW state hasn't changed while
	 * we were waiting for the safety timeout to pass.
	 */
	private boolean timeoutPassed = false;

	/**
	 * Once we detect that WILLOW is ready to shutdown we wait for a certain
	 * timeout to pass before stopping event dispatch threads.
	 */
	private static final int SAFETY_TIMEOUT = 1000;

	/**
	 * Constructor method is intentionally made private to secure 
	 * a single instance. Use getInstance() to reference it.
	 * 
	 * @see     EventAutoShutdown#getInstance
	 */
	private EventAutoShutdown()
	{
	}

	/**
	 * Returns reference to a single EventAutoShutdown instance.
	 */
	public static EventAutoShutdown getInstance()
	{
		return theInstance;
	}

	/**
	 * Add a specified thread to the set of busy event dispatch threads.
	 * If this set already contains the specified thread, the call leaves 
	 * this set unchanged and returns silently.
	 *
	 * @param thread thread to be added to this set, if not present.
	 * @see     EventAutoShutdown#notifyThreadFree
	 * @see     EventAutoShutdown#isReadyToShutdown
	 */
	public void notifyThreadBusy(final Thread thread)
	{
		synchronized (activationLock)
		{
			synchronized (mainLock)
			{
				if (blockerThread == null)
				{
					activateBlockerThread();
				}
				else if (isReadyToShutdown())
				{
					mainLock.notifyAll();
					timeoutPassed = false;
				}
				busyThreadSet.add(thread);
			}
		}
	}

	/**
	 * Remove a specified thread from the set of busy event dispatch threads.
	 * If this set doesn't contain the specified thread, the call leaves 
	 * this set unchanged and returns silently.
	 *
	 * @param thread thread to be removed from this set, if present.
	 * @see     EventAutoShutdown#notifyThreadBusy
	 * @see     EventAutoShutdown#isReadyToShutdown
	 */
	public void notifyThreadFree(final Thread thread)
	{
		synchronized (activationLock)
		{
			synchronized (mainLock)
			{
				busyThreadSet.remove(thread);
				if (isReadyToShutdown())
				{
					mainLock.notifyAll();
					timeoutPassed = false;
				}
			}
		}
	}

	/**
	 * Determine whether WILLOW is currently in ready-to-shutdown state.
	 * WILLOW is considered to be in ready-to-shutdown state if 
	 * <code>peerMap</code> is empty and <code>toolkitThreadBusy</code> 
	 * is false and <code>busyThreadSet</code> is empty. 
	 *
	 * @return true if WILLOW is in ready-to-shutdown state.
	 */
	private boolean isReadyToShutdown()
	{
		return (busyThreadSet.isEmpty());
	}

	/**
	 * Implementation of the Runnable interface.
	 * Incapsulates the blocker thread functionality. 
	 *
	 * @see     EventAutoShutdown#isReadyToShutdown
	 */
	public void run()
	{
		Thread currentThread = Thread.currentThread();
		boolean interrupted = false;
		synchronized (mainLock)
		{
			try
			{
				/* Notify that the thread is started. */
				mainLock.notifyAll();
				while (blockerThread == currentThread)
				{
					mainLock.wait();
					timeoutPassed = false;
					/*
					 * This loop is introduced to handle the following case:
					 * it is possible that while we are waiting for the 
					 * safety timeout to pass WILLOW state can change to 
					 * not-ready-to-shutdown and back to ready-to-shutdown.
					 * In this case we have to wait once again.
					 * NOTE: we shouldn't break into the outer loop 
					 * in this case, since we may never be notified 
					 * in an outer infinite wait at this point.
					 */
					while (isReadyToShutdown())
					{
						if (timeoutPassed)
						{
							timeoutPassed = false;
							blockerThread = null;
							break;
						}
						timeoutPassed = true;
						mainLock.wait(SAFETY_TIMEOUT);
					}
				}
			}
			catch (InterruptedException e)
			{
				interrupted = true;
			}
			finally
			{
				if (blockerThread == currentThread)
				{
					blockerThread = null;
				}
			}
		}
		//				if (!interrupted) {
		//						AppContext.stopEventDispatchThreads();
		//				}
	}

	public static WorkflowEvent getShutdownEvent()
	{
		return new WorkflowEvent(getInstance(), 0)
		{
		};
	}

	/**
	 * Creates and starts a new blocker thread. Doesn't return until
	 * the new blocker thread starts.
	 */
	private void activateBlockerThread()
	{
		Thread thread = new Thread(this, "WILLOW-Shutdown");
		thread.setDaemon(false);
		blockerThread = thread;
		thread.start();
		try
		{
			/* Wait for the blocker thread to start. */
			mainLock.wait();
		}
		catch (InterruptedException e)
		{
			System.err.println("WILLOW blocker activation interrupted:");
			e.printStackTrace();
		}
	}

} // class EventAutoShutdown
