/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.0 (the
 * "License"). You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * http://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */
package com.sun.jini.thread;

/**
 * This class provides a blocking mechanism that will not proceed while
 * some operation bounded by this object is in progress.  The thread
 * performing the operation invokes <code>start</code> and
 * <code>stop</code> on this object when it starts and stops the
 * operation in progress.  The thread that wants to block itself while
 * the operation is in progress invokes <code>waitWhileStarted</code>,
 * which will block if <code>start</code> has been invoked without a
 * matching <code>stop</code>.  The block will continue until a
 * matching <code>stop</code> is invoked.  Each <code>start</code> adds
 * one to the number of operations; each <code>stop</code> subtracts
 * one.  When the number of operations is zero, the operation bounded
 * by this object is not in progress, and <code>waitWhileStarted</code>
 * will return.
 * <p>
 * The operation can also be blocked by invoking <code>block</code> and
 * <code>unblock</code>, which nest like <code>start</code> and
 * <code>stop</code>.  When a <code>block</code> is invoked on this
 * object, no <code>start</code> will proceed until the matching
 * <code>unblock</code> call is made.  The reverse is also true: when a
 * <code>stop</code> call has been made on this object, no
 * <code>block</code> call will proceed until the matching
 * <code>stop</code> call is made.  A <code>waitUntilUnblocked</code>
 * method allows selective waiting for the unblocked state without
 * invoking <code>start</code>.
 * <p>
 * A <code>waitUntilQuiet</code> call waits until the object is neither
 * blocked nor stopped.
 *
 * @author Sun Microsystems, Inc.
 *
 */
public class InProgress {
    /** The state. */
    private int		count = 0;
    private String	name;
    private boolean	debug;

    public InProgress(String name) {
	this.name = name;
    }

    /** Signal the start of the operation this object bounds. */
    public synchronized void start() throws InterruptedException {
	while (count < 0)
	    wait();
	count++;
	if (debug)
	    show("start");
    }

    /** Signal the stop of the operation this object bounds. */
    public synchronized void stop() {
	if (--count == 0)
	    notifyAll();
	if (count < 0) {
	    count = 0;
	    throw new IllegalMonitorStateException("Too many stop invocations");
	    // set to a sane state in case the exception is caught and this
	    // object gets re-used
	}
	if (debug)
	    show("stop");
    }

    /**
     * Return <code>true</code> if at least one <code>start</code> has been
     * invoked without its corresponding <code>stop</code>.
     */
    public boolean inProgress() {
	return (count > 0);
    }

    /**
     * Wait if the operation bounded by this object is in progress, i.e.,
     * between a <code>start</code> and a <code>stop</code>.
     */
    public synchronized void waitWhileStarted() throws InterruptedException {
	if (debug)
	    show("waitWhileStarted");
	while (count > 0) {
	    wait();
	    if (debug)
		show("waitWhileStarted");
	}
    }

    /** Signal the blocking of the operation this object bounds. */
    public synchronized void block() throws InterruptedException {
	while (count > 0)
	    wait();
	count--;
	if (debug)
	    show("block");
    }

    /** Signal the unblocking of the operation this object bounds. */
    public synchronized void unblock() {
	if (++count == 0)
	    notifyAll();
	if (count > 0) {
	    count = 0;
	    throw new
		IllegalMonitorStateException("Too many unblock invocations");
	    // set to a sane state in case the exception is caught and this
	    // object gets re-used
	}
	if (debug)
	    show("unblock");
    }

    /**
     * Return <code>true</code> if at least one <code>block</code> has been
     * invoked without its corresponding <code>unblock</code>.
     */
    public boolean blocked() {
	return (count < 0);
    }

    /**
     * Wait if the operation bounded by this object has been blocked, i.e.,
     * between a <code>block</code> and a <code>unblock</code>.
     */
    public synchronized void waitWhileBlocked() throws InterruptedException {
	if (debug)
	    show("waitWhileBlocked");
	while (count < 0) {
	    wait();
	    if (debug)
		show("waitWhileBlocked");
	}
    }

    /**
     * Wait if the operation bounded by this object is either started
     * or blocked.
     */
    public synchronized void waitUntilQuiet() throws InterruptedException {
	if (debug)
	    show("waitUntilQuiet");
	while (count != 0) {
	    wait();
	    if (debug)
		show("waitUntilQuiet");
	}
    }

    /**
     * Set the debug mode on or off.
     */
    public void debug(boolean debugOn) {
	debug = debugOn;
    }

    /**
     * Show the current state of this object (used in debug mode).
     */
    private void show(String label) {
	System.out.print(name);
	System.out.print(":");
	System.out.print(label);
	System.out.print(": count = ");
	System.out.println(count);
    }
}
