/* WaitLock.java

	Purpose:
		
	Description:
		
	History:
		Wed Mar  2 10:55:54     2005, Created by tomyeh

Copyright (C) 2005 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.util;

import org.zkoss.lang.SystemException;
import org.zkoss.lang.PotentialDeadLockException;

/**
 * A simple lock used to implement load-on-demand mechanism.
 * Typical use: a thread, say A, checks whether a resource is loaded, and
 * put a WaitLock instance if not loaded yet. Then, another thread, say B,
 * if find WaitLock, it simply calls {@link #waitUntilUnlock} to wait.
 * Meanwhile, once A completes the loading, it put back the resource
 * and calls {@link #unlock}.
 *
 * <pre><code>WaitLock lock = null;
for (;;) {
	synchronized (map) {
		Object o = map.get(key);
		if (o == null) {
			map.put(key, lock = new WaitLock());
			break; //go to load resource
		}
	}
	if (o instanceof MyResource)
	 	return (MyResource)o;
	if (!((Lock)o).waitUntilUnlock(60000))
	 	log.waring("Takes too long");
}
//load resource
try {
	....
	synchronized (map) {
		map.put(key, resource);
	}
	return resource;
} catch (Throwable ex) {
	synchronized (map) {
		map.remove(key);
	}
	throw SystemException.Aide.wrap(ex);
} finally {
	lock.unlock();
}
</code></pre>
 *
 * @author tomyeh
 */
public class WaitLock {
	private final Thread _locker = Thread.currentThread();
	private boolean _unlocked;

	/** Once created, it is default to be locked.
	 * In other words, other thread's invocation of {@link #waitUntilUnlock}
	 * won't return until {@link #unlock} is called.
	 */
	public WaitLock() {
	}

	/** Waits this lock to unlock.
	 *
	 * @return whether it is unlocked successfully
	 * @exception SystemException if this thread is interrupted
	 * @exception PotentialDeadLockException if the thread itself creates
	 * this lock. In other words, it tried to wait for itself to complete.
	 */
	synchronized public boolean waitUntilUnlock(int timeout) {
		if (!_unlocked) {
			if (Thread.currentThread().equals(_locker))
				throw new PotentialDeadLockException("Wait for itself?");
			try {
				this.wait(timeout);
			} catch (InterruptedException ex) {
				throw SystemException.Aide.wrap(ex);
			}
		}
		return _unlocked;
	}
	/** Unlocks any other threads blocked by {@link #waitUntilUnlock}.
	 */
	synchronized public void unlock() {
		_unlocked = true;
		this.notifyAll(); //wake up all pending
	}
}
