001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.concurrent;
018
019import java.util.concurrent.ScheduledExecutorService;
020import java.util.concurrent.ScheduledFuture;
021import java.util.concurrent.ScheduledThreadPoolExecutor;
022import java.util.concurrent.TimeUnit;
023
024import org.apache.commons.lang3.Validate;
025
026/**
027 * A specialized <em>semaphore</em> implementation that provides a number of
028 * permits in a given time frame.
029 *
030 * <p>
031 * This class is similar to the {@code java.util.concurrent.Semaphore} class
032 * provided by the JDK in that it manages a configurable number of permits.
033 * Using the {@link #acquire()} method a permit can be requested by a thread.
034 * However, there is an additional timing dimension: there is no {@code
035 * release()} method for freeing a permit, but all permits are automatically
036 * released at the end of a configurable time frame. If a thread calls
037 * {@link #acquire()} and the available permits are already exhausted for this
038 * time frame, the thread is blocked. When the time frame ends all permits
039 * requested so far are restored, and blocking threads are waked up again, so
040 * that they can try to acquire a new permit. This basically means that in the
041 * specified time frame only the given number of operations is possible.
042 * </p>
043 * <p>
044 * A use case for this class is to artificially limit the load produced by a
045 * process. As an example consider an application that issues database queries
046 * on a production system in a background process to gather statistical
047 * information. This background processing should not produce so much database
048 * load that the functionality and the performance of the production system are
049 * impacted. Here a {@link TimedSemaphore} could be installed to guarantee that
050 * only a given number of database queries are issued per second.
051 * </p>
052 * <p>
053 * A thread class for performing database queries could look as follows:
054 * </p>
055 *
056 * <pre>
057 * public class StatisticsThread extends Thread {
058 *     // The semaphore for limiting database load.
059 *     private final TimedSemaphore semaphore;
060 *     // Create an instance and set the semaphore
061 *     public StatisticsThread(TimedSemaphore timedSemaphore) {
062 *         semaphore = timedSemaphore;
063 *     }
064 *     // Gather statistics
065 *     public void run() {
066 *         try {
067 *             while (true) {
068 *                 semaphore.acquire();   // limit database load
069 *                 performQuery();        // issue a query
070 *             }
071 *         } catch (InterruptedException) {
072 *             // fall through
073 *         }
074 *     }
075 *     ...
076 * }
077 * </pre>
078 *
079 * <p>
080 * The following code fragment shows how a {@link TimedSemaphore} is created
081 * that allows only 10 operations per second and passed to the statistics
082 * thread:
083 * </p>
084 *
085 * <pre>
086 * TimedSemaphore sem = new TimedSemaphore(1, TimeUnit.SECOND, 10);
087 * StatisticsThread thread = new StatisticsThread(sem);
088 * thread.start();
089 * </pre>
090 *
091 * <p>
092 * When creating an instance the time period for the semaphore must be
093 * specified. {@link TimedSemaphore} uses an executor service with a
094 * corresponding period to monitor this interval. The {@code
095 * ScheduledExecutorService} to be used for this purpose can be provided at
096 * construction time. Alternatively the class creates an internal executor
097 * service.
098 * </p>
099 * <p>
100 * Client code that uses {@link TimedSemaphore} has to call the
101 * {@link #acquire()} method in each processing step. {@link TimedSemaphore}
102 * keeps track of the number of invocations of the {@link #acquire()} method and
103 * blocks the calling thread if the counter exceeds the limit specified. When
104 * the timer signals the end of the time period the counter is reset and all
105 * waiting threads are released. Then another cycle can start.
106 * </p>
107 * <p>
108 * An alternative to {@code acquire()} is the {@link #tryAcquire()} method. This
109 * method checks whether the semaphore is under the specified limit and
110 * increases the internal counter if this is the case. The return value is then
111 * <strong>true</strong>, and the calling thread can continue with its action.
112 * If the semaphore is already at its limit, {@code tryAcquire()} immediately
113 * returns <strong>false</strong> without blocking; the calling thread must
114 * then abort its action. This usage scenario prevents blocking of threads.
115 * </p>
116 * <p>
117 * It is possible to modify the limit at any time using the
118 * {@link #setLimit(int)} method. This is useful if the load produced by an
119 * operation has to be adapted dynamically. In the example scenario with the
120 * thread collecting statistics it may make sense to specify a low limit during
121 * day time while allowing a higher load in the night time. Reducing the limit
122 * takes effect immediately by blocking incoming callers. If the limit is
123 * increased, waiting threads are not released immediately, but wake up when the
124 * timer runs out. Then, in the next period more processing steps can be
125 * performed without blocking. By setting the limit to 0 the semaphore can be
126 * switched off: in this mode the {@link #acquire()} method never blocks, but
127 * lets all callers pass directly.
128 * </p>
129 * <p>
130 * When the {@link TimedSemaphore} is no more needed its {@link #shutdown()}
131 * method should be called. This causes the periodic task that monitors the time
132 * interval to be canceled. If the {@link ScheduledExecutorService} has been
133 * created by the semaphore at construction time, it is also shut down.
134 * resources. After that {@link #acquire()} must not be called any more.
135 * </p>
136 *
137 * @since 3.0
138 */
139public class TimedSemaphore {
140    /**
141     * Constant for a value representing no limit. If the limit is set to a
142     * value less or equal this constant, the {@link TimedSemaphore} will be
143     * effectively switched off.
144     */
145    public static final int NO_LIMIT = 0;
146
147    /** Constant for the thread pool size for the executor. */
148    private static final int THREAD_POOL_SIZE = 1;
149
150    /** The executor service for managing the timer thread. */
151    private final ScheduledExecutorService executorService;
152
153    /** Stores the period for this timed semaphore. */
154    private final long period;
155
156    /** The time unit for the period. */
157    private final TimeUnit unit;
158
159    /** A flag whether the executor service was created by this object. */
160    private final boolean ownExecutor;
161
162    /** A future object representing the timer task. */
163    private ScheduledFuture<?> task; // @GuardedBy("this")
164
165    /** Stores the total number of invocations of the acquire() method. */
166    private long totalAcquireCount; // @GuardedBy("this")
167
168    /**
169     * The counter for the periods. This counter is increased every time a
170     * period ends.
171     */
172    private long periodCount; // @GuardedBy("this")
173
174    /** The limit. */
175    private int limit; // @GuardedBy("this")
176
177    /** The current counter. */
178    private int acquireCount;  // @GuardedBy("this")
179
180    /** The number of invocations of acquire() in the last period. */
181    private int lastCallsPerPeriod; // @GuardedBy("this")
182
183    /** A flag whether shutdown() was called. */
184    private boolean shutdown;  // @GuardedBy("this")
185
186    /**
187     * Creates a new instance of {@link TimedSemaphore} and initializes it with
188     * the given time period and the limit.
189     *
190     * @param timePeriod the time period
191     * @param timeUnit the unit for the period
192     * @param limit the limit for the semaphore
193     * @throws IllegalArgumentException if the period is less or equals 0
194     */
195    public TimedSemaphore(final long timePeriod, final TimeUnit timeUnit, final int limit) {
196        this(null, timePeriod, timeUnit, limit);
197    }
198
199    /**
200     * Creates a new instance of {@link TimedSemaphore} and initializes it with
201     * an executor service, the given time period, and the limit. The executor
202     * service will be used for creating a periodic task for monitoring the time
203     * period. It can be <b>null</b>, then a default service will be created.
204     *
205     * @param service the executor service
206     * @param timePeriod the time period
207     * @param timeUnit the unit for the period
208     * @param limit the limit for the semaphore
209     * @throws IllegalArgumentException if the period is less or equals 0
210     */
211    public TimedSemaphore(final ScheduledExecutorService service, final long timePeriod,
212            final TimeUnit timeUnit, final int limit) {
213        Validate.inclusiveBetween(1, Long.MAX_VALUE, timePeriod, "Time period must be greater than 0!");
214
215        period = timePeriod;
216        unit = timeUnit;
217
218        if (service != null) {
219            executorService = service;
220            ownExecutor = false;
221        } else {
222            final ScheduledThreadPoolExecutor s = new ScheduledThreadPoolExecutor(
223                    THREAD_POOL_SIZE);
224            s.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
225            s.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
226            executorService = s;
227            ownExecutor = true;
228        }
229
230        setLimit(limit);
231    }
232
233    /**
234     * Returns the limit enforced by this semaphore. The limit determines how
235     * many invocations of {@link #acquire()} are allowed within the monitored
236     * period.
237     *
238     * @return the limit
239     */
240    public final synchronized int getLimit() {
241        return limit;
242    }
243
244    /**
245     * Sets the limit. This is the number of times the {@link #acquire()} method
246     * can be called within the time period specified. If this limit is reached,
247     * further invocations of {@link #acquire()} will block. Setting the limit
248     * to a value &lt;= {@link #NO_LIMIT} will cause the limit to be disabled,
249     * i.e. an arbitrary number of{@link #acquire()} invocations is allowed in
250     * the time period.
251     *
252     * @param limit the limit
253     */
254    public final synchronized void setLimit(final int limit) {
255        this.limit = limit;
256    }
257
258    /**
259     * Initializes a shutdown. After that the object cannot be used anymore.
260     * This method can be invoked an arbitrary number of times. All invocations
261     * after the first one do not have any effect.
262     */
263    public synchronized void shutdown() {
264        if (!shutdown) {
265
266            if (ownExecutor) {
267                // if the executor was created by this instance, it has
268                // to be shutdown
269                getExecutorService().shutdownNow();
270            }
271            if (task != null) {
272                task.cancel(false);
273            }
274
275            shutdown = true;
276        }
277    }
278
279    /**
280     * Tests whether the {@link #shutdown()} method has been called on this
281     * object. If this method returns <b>true</b>, this instance cannot be used
282     * any longer.
283     *
284     * @return a flag whether a shutdown has been performed
285     */
286    public synchronized boolean isShutdown() {
287        return shutdown;
288    }
289
290    /**
291     * Acquires a permit from this semaphore. This method will block if
292     * the limit for the current period has already been reached. If
293     * {@link #shutdown()} has already been invoked, calling this method will
294     * cause an exception. The very first call of this method starts the timer
295     * task which monitors the time period set for this {@link TimedSemaphore}.
296     * From now on the semaphore is active.
297     *
298     * @throws InterruptedException if the thread gets interrupted
299     * @throws IllegalStateException if this semaphore is already shut down
300     */
301    public synchronized void acquire() throws InterruptedException {
302        prepareAcquire();
303
304        boolean canPass;
305        do {
306            canPass = acquirePermit();
307            if (!canPass) {
308                wait();
309            }
310        } while (!canPass);
311    }
312
313    /**
314     * Tries to acquire a permit from this semaphore. If the limit of this semaphore has
315     * not yet been reached, a permit is acquired, and this method returns
316     * <strong>true</strong>. Otherwise, this method returns immediately with the result
317     * <strong>false</strong>.
318     *
319     * @return <strong>true</strong> if a permit could be acquired; <strong>false</strong>
320     * otherwise
321     * @throws IllegalStateException if this semaphore is already shut down
322     * @since 3.5
323     */
324    public synchronized boolean tryAcquire() {
325        prepareAcquire();
326        return acquirePermit();
327    }
328
329    /**
330     * Returns the number of (successful) acquire invocations during the last
331     * period. This is the number of times the {@link #acquire()} method was
332     * called without blocking. This can be useful for testing or debugging
333     * purposes or to determine a meaningful threshold value. If a limit is set,
334     * the value returned by this method won't be greater than this limit.
335     *
336     * @return the number of non-blocking invocations of the {@link #acquire()}
337     * method
338     */
339    public synchronized int getLastAcquiresPerPeriod() {
340        return lastCallsPerPeriod;
341    }
342
343    /**
344     * Returns the number of invocations of the {@link #acquire()} method for
345     * the current period. This may be useful for testing or debugging purposes.
346     *
347     * @return the current number of {@link #acquire()} invocations
348     */
349    public synchronized int getAcquireCount() {
350        return acquireCount;
351    }
352
353    /**
354     * Returns the number of calls to the {@link #acquire()} method that can
355     * still be performed in the current period without blocking. This method
356     * can give an indication whether it is safe to call the {@link #acquire()}
357     * method without risking to be suspended. However, there is no guarantee
358     * that a subsequent call to {@link #acquire()} actually is not-blocking
359     * because in the meantime other threads may have invoked the semaphore.
360     *
361     * @return the current number of available {@link #acquire()} calls in the
362     * current period
363     */
364    public synchronized int getAvailablePermits() {
365        return getLimit() - getAcquireCount();
366    }
367
368    /**
369     * Returns the average number of successful (i.e. non-blocking)
370     * {@link #acquire()} invocations for the entire life-time of this {@code
371     * TimedSemaphore}. This method can be used for instance for statistical
372     * calculations.
373     *
374     * @return the average number of {@link #acquire()} invocations per time
375     * unit
376     */
377    public synchronized double getAverageCallsPerPeriod() {
378        return periodCount == 0 ? 0 : (double) totalAcquireCount
379                / (double) periodCount;
380    }
381
382    /**
383     * Returns the time period. This is the time monitored by this semaphore.
384     * Only a given number of invocations of the {@link #acquire()} method is
385     * possible in this period.
386     *
387     * @return the time period
388     */
389    public long getPeriod() {
390        return period;
391    }
392
393    /**
394     * Returns the time unit. This is the unit used by {@link #getPeriod()}.
395     *
396     * @return the time unit
397     */
398    public TimeUnit getUnit() {
399        return unit;
400    }
401
402    /**
403     * Returns the executor service used by this instance.
404     *
405     * @return the executor service
406     */
407    protected ScheduledExecutorService getExecutorService() {
408        return executorService;
409    }
410
411    /**
412     * Starts the timer. This method is called when {@link #acquire()} is called
413     * for the first time. It schedules a task to be executed at fixed rate to
414     * monitor the time period specified.
415     *
416     * @return a future object representing the task scheduled
417     */
418    protected ScheduledFuture<?> startTimer() {
419        return getExecutorService().scheduleAtFixedRate(this::endOfPeriod, getPeriod(), getPeriod(), getUnit());
420    }
421
422    /**
423     * The current time period is finished. This method is called by the timer
424     * used internally to monitor the time period. It resets the counter and
425     * releases the threads waiting for this barrier.
426     */
427    synchronized void endOfPeriod() {
428        lastCallsPerPeriod = acquireCount;
429        totalAcquireCount += acquireCount;
430        periodCount++;
431        acquireCount = 0;
432        notifyAll();
433    }
434
435    /**
436     * Prepares an acquire operation. Checks for the current state and starts the internal
437     * timer if necessary. This method must be called with the lock of this object held.
438     */
439    private void prepareAcquire() {
440        if (isShutdown()) {
441            throw new IllegalStateException("TimedSemaphore is shut down!");
442        }
443
444        if (task == null) {
445            task = startTimer();
446        }
447    }
448
449    /**
450     * Internal helper method for acquiring a permit. This method checks whether currently
451     * a permit can be acquired and - if so - increases the internal counter. The return
452     * value indicates whether a permit could be acquired. This method must be called with
453     * the lock of this object held.
454     *
455     * @return a flag whether a permit could be acquired
456     */
457    private boolean acquirePermit() {
458        if (getLimit() <= NO_LIMIT || acquireCount < getLimit()) {
459            acquireCount++;
460            return true;
461        }
462        return false;
463    }
464}