/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
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.
 *
 * 
 *
 */
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);
    }
}
