]> git.sesse.net Git - casparcg/blob - common/semaphore.h
Refactored executor to support try_begin_invoke which does not block until room is...
[casparcg] / common / semaphore.h
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #pragma once
23
24 #include <cmath>
25
26 #include <boost/noncopyable.hpp>
27 #include <boost/thread/mutex.hpp>
28 #include <boost/thread/condition.hpp>
29
30 namespace caspar {
31
32 template <class N, class Func>
33 void repeat_n(N times_to_repeat_block, const Func& func)
34 {
35         for (N i = 0; i < times_to_repeat_block; ++i)
36         {
37                 func();
38         }
39 }
40
41 /**
42  * Counting semaphore modelled after java.util.concurrent.Semaphore
43  */
44 class semaphore : boost::noncopyable
45 {
46         mutable boost::mutex mutex_;
47         unsigned int permits_;
48         boost::condition_variable permits_available_;
49 public:
50         /**
51          * Constructor.
52          *
53          * @param permits The initial number of permits.
54          */
55         semaphore(unsigned int permits)
56                 : permits_(permits)
57         {
58         }
59
60         /**
61          * Release a permit.
62          */
63         void release()
64         {
65                 boost::mutex::scoped_lock lock(mutex_);
66
67                 ++permits_;
68
69                 permits_available_.notify_one();
70         }
71
72         /**
73          * Release a permit.
74          *
75          * @param permits The number of permits to release.
76          */
77         void release(unsigned int permits)
78         {
79                 boost::mutex::scoped_lock lock(mutex_);
80
81                 permits_ += permits;
82
83                 repeat_n(permits, [this] { permits_available_.notify_one(); });
84         }
85
86         /**
87          * Acquire a permit. Will block until one becomes available if no permit is
88          * currently available.
89          */
90         void acquire()
91         {
92                 boost::mutex::scoped_lock lock(mutex_);
93
94                 while (permits_ == 0u)
95                 {
96                         permits_available_.wait(lock);
97                 }
98
99                 --permits_;
100         }
101
102         /**
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.
105          *
106          * @param permits The number of permits to acquire.
107          */
108         void acquire(unsigned int permits)
109         {
110                 boost::mutex::scoped_lock lock(mutex_);
111                 auto num_acquired = 0u;
112
113                 while (permits_ == 0u && num_acquired < permits)
114                 {
115                         permits_available_.wait(lock);
116
117                         auto num_wanted = permits - num_acquired;
118                         auto to_drain = std::min(num_wanted, permits_);
119
120                         permits_ -= to_drain;
121                         num_acquired += to_drain;
122                 }
123         }
124
125         /**
126          * Acquire one permits if permits are currently available. Does not block
127          * until one is available, but returns immediately if unavailable.
128          *
129          * @return true if a permit was acquired or false if no permits where
130          *         currently available.
131          */
132         bool try_acquire()
133         {
134                 boost::mutex::scoped_lock lock(mutex_);
135
136                 if (permits_ == 0u)
137                         return false;
138                 else
139                 {
140                         --permits_;
141
142                         return true;
143                 }
144         }
145
146         /**
147          * @return the current number of permits (may have changed at the time of
148          *         return).
149          */
150         unsigned int permits() const
151         {
152                 boost::mutex::scoped_lock lock(mutex_);
153
154                 return permits_;
155         }
156 };
157
158 }