2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Helge Norberg, helge.norberg@svt.se
26 #include <boost/noncopyable.hpp>
27 #include <boost/thread/mutex.hpp>
28 #include <boost/thread/condition_variable.hpp>
32 template <class N, class Func>
33 void repeat_n(N times_to_repeat_block, const Func& func)
35 for (N i = 0; i < times_to_repeat_block; ++i)
42 * Counting semaphore modelled after java.util.concurrent.Semaphore
44 class semaphore : boost::noncopyable
46 mutable boost::mutex mutex_;
47 unsigned int permits_;
48 boost::condition_variable_any permits_available_;
53 * @param permits The initial number of permits.
55 explicit semaphore(unsigned int permits)
65 boost::unique_lock<boost::mutex> lock(mutex_);
69 permits_available_.notify_one();
75 * @param permits The number of permits to release.
77 void release(unsigned int permits)
79 boost::unique_lock<boost::mutex> lock(mutex_);
83 repeat_n(permits, [this] { permits_available_.notify_one(); });
87 * Acquire a permit. Will block until one becomes available if no permit is
88 * currently available.
92 boost::unique_lock<boost::mutex> lock(mutex_);
94 while (permits_ == 0u)
96 permits_available_.wait(lock);
103 * Acquire a number of permits. Will block until the given number of
104 * permits has been acquired if not enough permits are currently available.
106 * @param permits The number of permits to acquire.
108 void acquire(unsigned int permits)
110 boost::unique_lock<boost::mutex> lock(mutex_);
111 auto num_acquired = 0u;
115 auto num_wanted = permits - num_acquired;
116 auto to_drain = std::min(num_wanted, permits_);
118 permits_ -= to_drain;
119 num_acquired += to_drain;
121 if (num_acquired == permits)
124 permits_available_.wait(lock);
129 * Acquire a number of permits. Will block until the given number of
130 * permits has been acquired if not enough permits are currently available
131 * or the timeout has passed.
133 * @param permits The number of permits to acquire.
134 * @param timeout The timeout (will be used for each permit).
136 * @return whether successfully acquired within timeout or not.
138 template <typename Rep, typename Period>
139 bool try_acquire(unsigned int permits, const boost::chrono::duration<Rep, Period>& timeout)
141 boost::unique_lock<boost::mutex> lock(mutex_);
142 auto num_acquired = 0u;
146 auto num_wanted = permits - num_acquired;
147 auto to_drain = std::min(num_wanted, permits_);
149 permits_ -= to_drain;
150 num_acquired += to_drain;
152 if (num_acquired == permits)
155 if (permits_available_.wait_for(lock, timeout) == boost::cv_status::timeout)
158 release(num_acquired);
167 * Acquire one permits if permits are currently available. Does not block
168 * until one is available, but returns immediately if unavailable.
170 * @return true if a permit was acquired or false if no permits where
171 * currently available.
175 boost::unique_lock<boost::mutex> lock(mutex_);
188 * @return the current number of permits (may have changed at the time of
191 unsigned int permits() const
193 boost::unique_lock<boost::mutex> lock(mutex_);
200 * Enables RAII-style acquire/release on scope exit unless committed.
202 class acquire_transaction : boost::noncopyable
204 semaphore& semaphore_;
210 * @param semaphore The semaphore to acquire one permit from.
211 * @param already_acquired Whether a permit has already been acquired or not.
213 acquire_transaction(semaphore& semaphore, bool already_acquired = false)
214 : semaphore_(semaphore)
217 if (!already_acquired)
218 semaphore_.acquire();
222 * Destructor that will release one permit if commit() has not been called.
224 ~acquire_transaction()
227 semaphore_.release();
231 * Ensure that the acquired permit is kept on destruction.