//
// $Id: Throttle.java,v 1.2 2001/08/11 22:43:29 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
// 
// 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 com.samskivert.util;

/**
 * A throttle is used to prevent code from attempting a particular
 * operation too often. Often it is desirable to retry an operation under
 * failure conditions, but simplistic approaches to retrying operations
 * can lead to large numbers of spurious attempts to do something that
 * will obviously fail. The throttle class provides a mechanism for
 * limiting such attempts by measuring whether or not an activity has been
 * performed N times in the last M seconds. The user of the class decides
 * the appropriate throttle parameters and then simply calls through to
 * throttle to determine whether or not to go ahead with the operation.
 *
 * <p> For example:
 *
 * <pre>
 * protected Throttle _throttle = new Throttle(5, 60);
 *
 * public void performOp ()
 *     throws UnavailableException
 * {
 *     if (_throttle.throttleOp()) {
 *         throw new UnavailableException();
 *     }
 *
 *     // perform operation
 * }
 * </pre>
 */
public class Throttle
{
    /**
     * Constructs a new throttle instance that will allow the specified
     * number of operations to proceed within the specified period (the
     * period is measured in seconds).
     *
     * <p> As operations and period define a ratio, use the smallest value
     * possible for operations as an array is created to track the time at
     * which each operation was performed (e.g. use 6 ops per 10 seconds
     * rather than 60 ops per 100 seconds if possible). However, note that
     * you may not always want to reduce the ratio as much as possible if
     * you wish to allow bursts of operations up to some large value.
     */
    public Throttle (int operations, int period)
    {
        _ops = new long[operations];
        _period = 1000L * (long)period;
    }

    /**
     * Registers an attempt at an operation and returns true if the
     * operation should be performed or false if it should be throttled
     * (meaning N operations have already been performed in the last M
     * seconds).
     *
     * @return true if the throttle is activated, false if the operation
     * can proceed.
     */
    public boolean throttleOp ()
    {
        long now = System.currentTimeMillis();

        // if the oldest operation was performed less than _period ago, we
        // need to throttle
        if ((now - _ops[_lastOp]) < _period) {
            return true;
        }

        // otherwise overwrite the oldest operation with the current time
        // and move the oldest operation pointer to the second oldest
        // operation (which is now the oldest as we overwrote the oldest)
        _ops[_lastOp] = now;
        _lastOp = (_lastOp + 1) % _ops.length;
        return false;
    }

    public static void main (String[] args)
    {
        Throttle throttle = new Throttle(5, 10);

        // try doing one operation per second and we should hit the
        // throttle on the sixth operation and then kick in again on the
        // eleventh, only to stop again on the fifteenth
        for (int i = 0; i < 20; i++) {
            System.out.println((i+1) + ". Throttle: " +
                               throttle.throttleOp());
            // pause for a sec
            try { Thread.sleep(1000L); }
            catch (InterruptedException ie) {}
        }
    }

    protected long[] _ops;
    protected int _lastOp;
    protected long _period;
}

@@ -26,29 +26,29 @@
 v 1.
-2
+3
  200
-1/08/11 22:43:29
+2/12/04 02:54:58
  mdb
@@ -3077,26 +3077,354 @@
     
-long now =
+return throttleOp(System.currentTimeMillis());%0A    %7D%0A%0A    /**%0A     * Registers an attempt at an operation and returns true if the%0A     * operation should be performed or false if it should be throttled%0A     * (meaning N operations have already been performed in the last M%0A     * seconds). For systems that don't wish to use %7B@link%0A     *
  System
-.
+#
 curr
@@ -3436,20 +3436,558 @@
 meMillis
-();%0A
+%7D (opting in favor for some custom timing%0A     * mechanism that is more accurate that %7B@link%0A     * System#currentTimeMillis%7D) or those that already have the time,%0A     * they can avoid an unnecessary call to %7B@link%0A     * System#currentTimeMillis%7D by using this version of the method.%0A     *%0A     * @param timeStamp the timestamp at which this operation is being%0A     * attempted.%0A     *%0A     * @return true if the throttle is activated, false if the operation%0A     * can proceed.%0A     */%0A    public boolean throttleOp (long timeStamp)%0A    %7B
 %0A       
@@ -4095,19 +4095,25 @@
    if ((
-now
+timeStamp
  - _ops%5B
@@ -4413,19 +4413,25 @@
 stOp%5D = 
-now
+timeStamp
 ;%0A      

Provide a way to use the throttle with ones own timestamps.