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