//jTDS JDBC Driver for Microsoft SQL Server and Sybase
//Copyright (C) 2004 The jTDS Project
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
package net.sourceforge.jtds.jdbc;

/**
 * Simple semaphore class used to serialize access requests over the network
 * connection.
 * <p/>
 * Based on the code originally written by Doug Lea. Once JDK 1.5 is the
 * standard this class can be replaced by the
 * <code>java.util.concurrent.Sempahore</code> class.
 *
 * @author  Mike Hutchinson
 * @version $Id: Semaphore.java,v 1.0, 2006-03-15 17:47:16Z, Robert Worsnop$
 */
public class Semaphore {
    /**
     * Current number of available permits.
     */
    protected long permits;

    /**
     * Create a Semaphore with the given initial number of permits. Using a
     * seed of one makes the semaphore act as a mutual exclusion lock. Negative
     * seeds are also allowed, in which case no acquires will proceed until the
     * number of releases has pushed the number of permits past 0.
     */
    public Semaphore(long initialPermits) {
        permits = initialPermits;
    }

    /**
     * Wait until a permit is available, and take one.
     */
    public void acquire() throws InterruptedException {
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }

        synchronized (this) {
            try {
                while (permits <= 0) {
                    wait();
                }
                --permits;
            } catch (InterruptedException ex) {
                notify();
                throw ex;
            }
        }
    }

    /**
     * Wait at most msecs millisconds for a permit.
     */
    public boolean attempt(long msecs) throws InterruptedException {
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }

        synchronized (this) {
            if (permits > 0) {
                --permits;
                return true;
            } else if (msecs <= 0) {
                return false;
            } else {
                try {
                    long startTime = System.currentTimeMillis();
                    long waitTime = msecs;

                    while (true) {
                        wait(waitTime);

                        if (permits > 0) {
                            --permits;
                            return true;
                        } else {
                            waitTime = msecs - (System.currentTimeMillis() - startTime);
                            if (waitTime <= 0) {
                                return false;
                            }
                        }
                    }
                } catch (InterruptedException ex) {
                    notify();
                    throw ex;
                }
            }
        }
    }

    /**
     * Release a permit.
     */
    public synchronized void release() {
        ++permits;
        notify();
    }

    /**
     * Release N permits. <code>release(n)</code> is equivalent in effect to:
     * <pre>
     *   for (int i = 0; i < n; ++i) release();
     * </pre>
     * <p/>
     * But may be more efficient in some semaphore implementations.
     *
     * @exception IllegalArgumentException if n is negative
     */
    public synchronized void release(long n) {
        if (n < 0) {
            throw new IllegalArgumentException("Negative argument");
        }

        permits += n;
        for (long i = 0; i < n; ++i) {
            notify();
        }
    }

    /**
     * Return the current number of available permits. Returns an accurate, but
     * possibly unstable value, that may change immediately after returning.
     */
    public synchronized long permits() {
        return permits;
    }
}
