| /* |
| * 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. |
| */ |
| |
| #ifndef _DECAF_UTIL_CONCURRENT_SEMAPHORE_H_ |
| #define _DECAF_UTIL_CONCURRENT_SEMAPHORE_H_ |
| |
| #include <decaf/util/Config.h> |
| |
| #include <decaf/lang/exceptions/InterruptedException.h> |
| #include <decaf/lang/exceptions/RuntimeException.h> |
| #include <decaf/lang/exceptions/IllegalArgumentException.h> |
| |
| #include <decaf/util/Collection.h> |
| #include <decaf/util/concurrent/TimeUnit.h> |
| |
| namespace decaf { |
| namespace util { |
| namespace concurrent { |
| |
| class SemSync; |
| |
| /** |
| * A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each acquire() |
| * blocks if necessary until a permit is available, and then takes it. Each release() adds a |
| * permit, potentially releasing a blocking acquirer. However, no actual permit objects are |
| * used; the Semaphore just keeps a count of the number available and acts accordingly. |
| * |
| * Semaphores are often used to restrict the number of threads than can access some (physical |
| * or logical) resource. |
| * |
| * class Pool { |
| * private: |
| * |
| * static const int MAX_AVAILABLE = 100; |
| * Semaphore available; |
| * |
| * std::vector<std::string> items; |
| * std::vector<bool> used; |
| * |
| * Mutex lock; |
| * |
| * public: |
| * |
| * Pool() : available( MAX_AVAILABLE, true ) { |
| * used.resize( MAX_AVAILABLE ); |
| * items.resize( MAX_AVAILABLE ); |
| * } |
| * |
| * std::string getItem() throws InterruptedException { |
| * available.acquire(); |
| * return getNextAvailableItem(); |
| * } |
| * |
| * void putItem( std::string x ) { |
| * if( markAsUnused(x) ) { |
| * available.release(); |
| * } |
| * } |
| * |
| * std::string getNextAvailableItem() { |
| * |
| * synchronized( &lock ) { |
| * for( int i = 0; i < MAX_AVAILABLE; ++i ) { |
| * if( !used[i] ) { |
| * used[i] = true; |
| * return items[i]; |
| * } |
| * } |
| * |
| * return std::string(); // not reached |
| * } |
| * |
| * bool markAsUnused( const std::string& item ) { |
| * synchronized( &lock ) { |
| * for( int i = 0; i < MAX_AVAILABLE; ++i ) { |
| * if( item == items[i] ) { |
| * if( used[i] ) { |
| * used[i] = false; |
| * return true; |
| * } else |
| * return false; |
| * } |
| * } |
| * } |
| * return false; |
| * } |
| * }; |
| * |
| * Before obtaining an item each thread must acquire a permit from the semaphore, guaranteeing |
| * that an item is available for use. When the thread has finished with the item it is returned |
| * back to the pool and a permit is returned to the semaphore, allowing another thread to acquire |
| * that item. Note that no synchronization lock is held when acquire() is called as that would |
| * prevent an item from being returned to the pool. The semaphore encapsulates the synchronization |
| * needed to restrict access to the pool, separately from any synchronization needed to maintain |
| * the consistency of the pool itself. |
| * |
| * A semaphore initialized to one, and which is used such that it only has at most one permit |
| * available, can serve as a mutual exclusion lock. This is more commonly known as a binary |
| * semaphore, because it only has two states: one permit available, or zero permits available. |
| * When used in this way, the binary semaphore has the property (unlike many Lock implementations), |
| * that the "lock" can be released by a thread other than the owner (as semaphores have no notion |
| * of ownership). This can be useful in some specialized contexts, such as deadlock recovery. |
| * |
| * The constructor for this class optionally accepts a fairness parameter. When set false, this |
| * class makes no guarantees about the order in which threads acquire permits. In particular, |
| * barging is permitted, that is, a thread invoking acquire() can be allocated a permit ahead of |
| * a thread that has been waiting - logically the new thread places itself at the head of the |
| * queue of waiting threads. When fairness is set true, the semaphore guarantees that threads |
| * invoking any of the acquire methods are selected to obtain permits in the order in which their |
| * invocation of those methods was processed (first-in-first-out; FIFO). Note that FIFO ordering |
| * necessarily applies to specific internal points of execution within these methods. So, it is |
| * possible for one thread to invoke acquire before another, but reach the ordering point after |
| * the other, and similarly upon return from the method. Also note that the untimed tryAcquire |
| * methods do not honor the fairness setting, but will take any permits that are available. |
| * |
| * Generally, semaphores used to control resource access should be initialized as fair, to ensure |
| * that no thread is starved out from accessing a resource. When using semaphores for other kinds |
| * of synchronization control, the throughput advantages of non-fair ordering often outweigh |
| * fairness considerations. |
| * |
| * This class also provides convenience methods to acquire and release multiple permits at a time. |
| * Beware of the increased risk of indefinitely postponing when these methods are used without |
| * fairness set true. |
| * |
| * @since 1.0 |
| */ |
| class DECAF_API Semaphore { |
| private: |
| |
| SemSync* sync; |
| |
| private: |
| |
| Semaphore( const Semaphore& ); |
| Semaphore& operator= ( const Semaphore& ); |
| |
| public: |
| |
| /** |
| * Creates a Semaphore with the given number of permits and nonfair fairness setting. |
| * |
| * @param permits the initial number of permits available. This value may be negative, in |
| * which case releases must occur before any acquires will be granted. |
| */ |
| Semaphore( int permits ); |
| |
| /** |
| * Creates a Semaphore with the given number of permits and the given fairness setting. |
| * |
| * @param permits the initial number of permits available. This value may be negative, in |
| * which case releases must occur before any acquires will be granted. |
| * @param fair true if this semaphore will guarantee first-in first-out granting of |
| * permits under contention, else false |
| */ |
| Semaphore( int permits, bool fair ); |
| |
| virtual ~Semaphore(); |
| |
| /** |
| * Acquires a permit from this semaphore, blocking until one is available, or the thread |
| * is interrupted. |
| * |
| * Acquires a permit, if one is available and returns immediately, reducing the number of |
| * available permits by one. |
| * |
| * If no permit is available then the current thread becomes disabled for thread scheduling |
| * purposes and lies dormant until one of two things happens: |
| * |
| * * Some other thread invokes the release() method for this semaphore and the current |
| * thread is next to be assigned a permit; or |
| * * Some other thread interrupts the current thread. |
| * |
| * If the current thread: |
| * |
| * * has its interrupted status set on entry to this method; or |
| * * is interrupted while waiting for a permit, |
| * |
| * then InterruptedException is thrown and the current thread's interrupted status is cleared. |
| * |
| * @throw InterruptedException - if the current thread is interrupted. |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| void acquire(); |
| |
| /** |
| * Acquires a permit from this semaphore, blocking until one is available. |
| * |
| * Acquires a permit, if one is available and returns immediately, reducing the number of |
| * available permits by one. |
| * |
| * If no permit is available then the current thread becomes disabled for thread scheduling |
| * purposes and lies dormant until some other thread invokes the release() method for this |
| * semaphore and the current thread is next to be assigned a permit. |
| * |
| * If the current thread is interrupted while waiting for a permit then it will continue to |
| * wait, but the time at which the thread is assigned a permit may change compared to the |
| * time it would have received the permit had no interruption occurred. When the thread does |
| * return from this method its interrupt status will be set. |
| * |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| void acquireUninterruptibly(); |
| |
| /** |
| * Acquires a permit from this semaphore, only if one is available at the time of invocation. |
| * |
| * Acquires a permit, if one is available and returns immediately, with the value true, reducing |
| * the number of available permits by one. |
| * |
| * If no permit is available then this method will return immediately with the value false. |
| * |
| * Even when this semaphore has been set to use a fair ordering policy, a call to tryAcquire() |
| * will immediately acquire a permit if one is available, whether or not other threads are |
| * currently waiting. This "barging" behavior can be useful in certain circumstances, even |
| * though it breaks fairness. If you want to honor the fairness setting, then use |
| * tryAcquire(0, TimeUnit.SECONDS) which is almost equivalent (it also detects interruption). |
| * |
| * @return true if a permit was acquired and false otherwise |
| * |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| bool tryAcquire(); |
| |
| /** |
| * Acquires a permit from this semaphore, if one becomes available within the given waiting |
| * time and the current thread has not been interrupted. |
| * |
| * Acquires a permit, if one is available and returns immediately, with the value true, |
| * reducing the number of available permits by one. |
| * |
| * If no permit is available then the current thread becomes disabled for thread scheduling |
| * purposes and lies dormant until one of three things happens: |
| * |
| * * Some other thread invokes the release() method for this semaphore and the current |
| * thread is next to be assigned a permit; or |
| * * Some other thread interrupts the current thread; or |
| * * The specified waiting time elapses. |
| * |
| * If a permit is acquired then the value true is returned. |
| * |
| * If the current thread: |
| * |
| * * has its interrupted status set on entry to this method; or |
| * * is interrupted while waiting to acquire a permit, |
| * |
| * then InterruptedException is thrown and the current thread's interrupted status is cleared. |
| * |
| * If the specified waiting time elapses then the value false is returned. If the time is less |
| * than or equal to zero, the method will not wait at all. |
| * |
| * @param timeout the maximum time to wait for a permit |
| * @param unit the time unit of the timeout argument |
| * |
| * @return true if a permit was acquired and false if the waiting time elapsed before a permit |
| * was acquired |
| * |
| * @throw InterruptedException if the current thread is interrupted. |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| bool tryAcquire( long long timeout, const TimeUnit& unit ); |
| |
| /** |
| * Releases a permit, returning it to the semaphore. |
| * |
| * Releases a permit, increasing the number of available permits by one. If any threads are |
| * trying to acquire a permit, then one is selected and given the permit that was just |
| * released. That thread is (re)enabled for thread scheduling purposes. |
| * |
| * There is no requirement that a thread that releases a permit must have acquired that permit |
| * by calling acquire(). Correct usage of a semaphore is established by programming convention |
| * in the application. |
| * |
| * @throw RuntimeException if an unexpected error occurs while releasing the Semaphore. |
| */ |
| void release(); |
| |
| /** |
| * Acquires the given number of permits from this semaphore, blocking until all are available, |
| * or the thread is interrupted. |
| * |
| * Acquires the given number of permits, if they are available, and returns immediately, |
| * reducing the number of available permits by the given amount. |
| * |
| * If insufficient permits are available then the current thread becomes disabled for thread |
| * scheduling purposes and lies dormant until one of two things happens: |
| * |
| * * Some other thread invokes one of the release methods for this semaphore, the current |
| * thread is next to be assigned permits and the number of available permits satisfies |
| * this request; or |
| * * Some other thread interrupts the current thread. |
| * |
| * If the current thread: |
| * |
| * * has its interrupted status set on entry to this method; or |
| * * is interrupted while waiting for a permit, |
| * |
| * then InterruptedException is thrown and the current thread's interrupted status is cleared. |
| * Any permits that were to be assigned to this thread are instead assigned to other threads |
| * trying to acquire permits, as if permits had been made available by a call to release(). |
| * |
| * @param permits the number of permits to acquire. |
| * |
| * @throw InterruptedException if the current thread is interrupted. |
| * @throw IllegalArgumentException if the permits argument is negative. |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| void acquire( int permits ); |
| |
| /** |
| * Acquires the given number of permits from this semaphore, blocking until all are available. |
| * |
| * Acquires the given number of permits, if they are available, and returns immediately, |
| * reducing the number of available permits by the given amount. |
| * |
| * If insufficient permits are available then the current thread becomes disabled for thread |
| * scheduling purposes and lies dormant until some other thread invokes one of the release |
| * methods for this semaphore, the current thread is next to be assigned permits and the number |
| * of available permits satisfies this request. |
| * |
| * If the current thread is interrupted while waiting for permits then it will continue to wait |
| * and its position in the queue is not affected. When the thread does return from this method |
| * its interrupt status will be set. |
| * |
| * @param permits the number of permits to acquire. |
| * |
| * @throw IllegalArgumentException if the permits argument is negative. |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| void acquireUninterruptibly( int permits ); |
| |
| /** |
| * Acquires the given number of permits from this semaphore, only if all are available at the |
| * time of invocation. |
| * |
| * Acquires the given number of permits, if they are available, and returns immediately, with |
| * the value true, reducing the number of available permits by the given amount. |
| * |
| * If insufficient permits are available then this method will return immediately with the value |
| * false and the number of available permits is unchanged. |
| * |
| * Even when this semaphore has been set to use a fair ordering policy, a call to tryAcquire |
| * will immediately acquire a permit if one is available, whether or not other threads are |
| * currently waiting. This "barging" behavior can be useful in certain circumstances, even |
| * though it breaks fairness. If you want to honor the fairness setting, then use |
| * tryAcquire(permits, 0, TimeUnit.SECONDS) which is almost equivalent (it also detects |
| * interruption). |
| * |
| * @param permits the number of permits to acquire |
| * |
| * @return true if the permits were acquired and false otherwise. |
| * |
| * @throw IllegalArgumentException if the permits argument is negative. |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| bool tryAcquire( int permits ); |
| |
| /** |
| * Acquires the given number of permits from this semaphore, if all become available within |
| * the given waiting time and the current thread has not been interrupted. |
| * |
| * Acquires the given number of permits, if they are available and returns immediately, |
| * with the value true, reducing the number of available permits by the given amount. |
| * |
| * If insufficient permits are available then the current thread becomes disabled for thread |
| * scheduling purposes and lies dormant until one of three things happens: |
| * |
| * * Some other thread invokes one of the release methods for this semaphore, the current |
| * thread is next to be assigned permits and the number of available permits satisfies |
| * this request; or |
| * * Some other thread interrupts the current thread; or |
| * * The specified waiting time elapses. |
| * |
| * If the permits are acquired then the value true is returned. |
| * |
| * If the current thread: |
| * |
| * * has its interrupted status set on entry to this method; or |
| * * is interrupted while waiting to acquire the permits, |
| * |
| * then InterruptedException is thrown and the current thread's interrupted status is cleared. |
| * Any permits that were to be assigned to this thread, are instead assigned to other threads |
| * trying to acquire permits, as if the permits had been made available by a call to release(). |
| * |
| * If the specified waiting time elapses then the value false is returned. If the time is less |
| * than or equal to zero, the method will not wait at all. Any permits that were to be assigned |
| * to this thread, are instead assigned to other threads trying to acquire permits, as if the |
| * permits had been made available by a call to release(). |
| * |
| * @param permits the number of permits to acquire |
| * @param timeout the maximum amount of time to wait to acquire the permits. |
| * @param unit the units that the timeout param represents. |
| * |
| * @return true if all permits were acquired and false if the waiting time elapsed before |
| * all permits were acquired |
| * |
| * @throw IllegalArgumentException if the permits argument is negative. |
| * @throw RuntimeException if an unexpected error occurs while acquiring the Semaphore. |
| */ |
| bool tryAcquire( int permits, long long timeout, const TimeUnit& unit ); |
| |
| /** |
| * Releases the given number of permits, returning them to the semaphore. |
| * |
| * Releases the given number of permits, increasing the number of available permits by that |
| * amount. If any threads are trying to acquire permits, then one is selected and given the |
| * permits that were just released. If the number of available permits satisfies that thread's |
| * request then that thread is (re)enabled for thread scheduling purposes; otherwise the |
| * thread will wait until sufficient permits are available. If there are still permits |
| * available after this thread's request has been satisfied, then those permits are assigned |
| * in turn to other threads trying to acquire permits. |
| * |
| * @param permits the number of permits to release |
| * |
| * @throw IllegalArgumentException if the permits argument is negative. |
| * @throw RuntimeException if an unexpected error occurs while releasing the Semaphore. |
| */ |
| void release( int permits ); |
| |
| /** |
| * Returns the current number of permits available in this semaphore. |
| * |
| * This method is typically used for debugging and testing purposes. |
| * |
| * @return the number of permits available in this semaphore |
| */ |
| int availablePermits() const; |
| |
| /** |
| * Acquires and returns all permits that are immediately available. |
| * |
| * @return the number of permits acquired |
| * |
| * @throw RuntimeException if an unexpected error occurs while draining the Semaphore. |
| */ |
| int drainPermits(); |
| |
| /** |
| * @return true if this semaphore has fairness set true |
| */ |
| bool isFair() const; |
| |
| /** |
| * Returns a string identifying this semaphore, as well as its state. The state, in |
| * brackets, includes the String "Permits =" followed by the number of permits. |
| * |
| * @return a string identifying this semaphore, as well as its state |
| */ |
| std::string toString() const; |
| |
| /** |
| * Gets an estimated count of the number of threads that are currently waiting to acquire, this |
| * value changes dynamically so the result of this method can be invalid immediately after it |
| * is called. |
| * |
| * @return an estimate of the number of waiting threads. |
| */ |
| int getQueueLength() const; |
| |
| /** |
| * @return true if there are threads that are currently waiting to acquire this Semaphore. |
| */ |
| bool hasQueuedThreads() const; |
| |
| protected: |
| |
| /** |
| * Reduces the number of available permits which can be useful for subclasses. If the subclass |
| * is tracking a resource that is transiently available this method can be used to modify the |
| * Semaphore to reflect that resources current state. This method does not block waiting for |
| * the number of permits to be available, unlike the acquire method. |
| * |
| * @param reduceBy |
| * The number of permits to remove from the current available set. |
| * |
| * @throws IllegalArgumentException if the param passed in negative. |
| */ |
| void reducePermits(int reduceBy); |
| |
| /** |
| * Creates and returns a new Collection object that contains a best effort snapshot of the |
| * threads that are currently waiting to acquire. |
| * |
| * @return a Collection pointer that contains waiting threads for lock acquisition. |
| * The caller owns the returned pointer. |
| */ |
| decaf::util::Collection<decaf::lang::Thread*>* getQueuedThreads() const; |
| |
| }; |
| |
| }}} |
| |
| #endif /* _DECAF_UTIL_CONCURRENT_SEMAPHORE_H_ */ |