]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | ////////////////////////////////////////////////////////////////////////////// |
2 | // Code based on Howard Hinnant's shared_mutex class | |
3 | // | |
4 | // (C) Copyright Howard Hinnant 2007-2010. Distributed under the Boost | |
5 | // Software License, Version 1.0. (see http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost | |
8 | // Software License, Version 1.0. (See accompanying file | |
9 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
10 | // | |
11 | // See http://www.boost.org/libs/interprocess for documentation. | |
12 | // | |
13 | ////////////////////////////////////////////////////////////////////////////// | |
14 | ||
15 | #ifndef BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP | |
16 | #define BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP | |
17 | ||
18 | #ifndef BOOST_CONFIG_HPP | |
19 | # include <boost/config.hpp> | |
20 | #endif | |
21 | # | |
22 | #if defined(BOOST_HAS_PRAGMA_ONCE) | |
23 | # pragma once | |
24 | #endif | |
25 | ||
26 | #include <boost/interprocess/detail/config_begin.hpp> | |
27 | #include <boost/interprocess/detail/workaround.hpp> | |
28 | #include <boost/interprocess/sync/scoped_lock.hpp> | |
7c673cae FG |
29 | #include <boost/interprocess/sync/interprocess_mutex.hpp> |
30 | #include <boost/interprocess/sync/interprocess_condition.hpp> | |
31 | #include <climits> | |
32 | ||
33 | ||
34 | //!\file | |
35 | //!Describes interprocess_sharable_mutex class | |
36 | ||
37 | namespace boost { | |
38 | namespace interprocess { | |
39 | ||
40 | //!Wraps a interprocess_sharable_mutex that can be placed in shared memory and can be | |
41 | //!shared between processes. Allows timed lock tries | |
42 | class interprocess_sharable_mutex | |
43 | { | |
44 | //Non-copyable | |
45 | interprocess_sharable_mutex(const interprocess_sharable_mutex &); | |
46 | interprocess_sharable_mutex &operator=(const interprocess_sharable_mutex &); | |
47 | ||
48 | friend class interprocess_condition; | |
49 | public: | |
50 | ||
51 | //!Constructs the sharable lock. | |
52 | //!Throws interprocess_exception on error. | |
53 | interprocess_sharable_mutex(); | |
54 | ||
55 | //!Destroys the sharable lock. | |
56 | //!Does not throw. | |
57 | ~interprocess_sharable_mutex(); | |
58 | ||
59 | //Exclusive locking | |
60 | ||
1e59de90 TL |
61 | //!Requires: The calling thread does not own the mutex. |
62 | //! | |
7c673cae FG |
63 | //!Effects: The calling thread tries to obtain exclusive ownership of the mutex, |
64 | //! and if another thread has exclusive or sharable ownership of | |
65 | //! the mutex, it waits until it can obtain the ownership. | |
66 | //!Throws: interprocess_exception on error. | |
1e59de90 TL |
67 | //! |
68 | //!Note: A program may deadlock if the thread that has ownership calls | |
69 | //! this function. If the implementation can detect the deadlock, | |
70 | //! an exception could be thrown. | |
7c673cae FG |
71 | void lock(); |
72 | ||
1e59de90 TL |
73 | //!Requires: The calling thread does not own the mutex. |
74 | //! | |
7c673cae FG |
75 | //!Effects: The calling thread tries to acquire exclusive ownership of the mutex |
76 | //! without waiting. If no other thread has exclusive or sharable | |
77 | //! ownership of the mutex this succeeds. | |
78 | //!Returns: If it can acquire exclusive ownership immediately returns true. | |
79 | //! If it has to wait, returns false. | |
80 | //!Throws: interprocess_exception on error. | |
1e59de90 TL |
81 | //! |
82 | //!Note: A program may deadlock if the thread that has ownership calls | |
83 | //! this function. If the implementation can detect the deadlock, | |
84 | //! an exception could be thrown. | |
7c673cae FG |
85 | bool try_lock(); |
86 | ||
1e59de90 TL |
87 | //!Requires: The calling thread does not own the mutex. |
88 | //! | |
7c673cae FG |
89 | //!Effects: The calling thread tries to acquire exclusive ownership of the mutex |
90 | //! waiting if necessary until no other thread has exclusive or sharable | |
91 | //! ownership of the mutex or abs_time is reached. | |
92 | //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false. | |
93 | //!Throws: interprocess_exception on error. | |
1e59de90 TL |
94 | //! |
95 | //!Note: A program may deadlock if the thread that has ownership calls | |
96 | //! this function. If the implementation can detect the deadlock, | |
97 | //! an exception could be thrown. | |
98 | template<class TimePoint> | |
99 | bool timed_lock(const TimePoint &abs_time); | |
100 | ||
101 | //!Same as `timed_lock`, but this function is modeled after the | |
102 | //!standard library interface. | |
103 | template<class TimePoint> bool try_lock_until(const TimePoint &abs_time) | |
104 | { return this->timed_lock(abs_time); } | |
105 | ||
106 | //!Same as `timed_lock`, but this function is modeled after the | |
107 | //!standard library interface. | |
108 | template<class Duration> bool try_lock_for(const Duration &dur) | |
109 | { return this->timed_lock(ipcdetail::duration_to_ustime(dur)); } | |
7c673cae FG |
110 | |
111 | //!Precondition: The thread must have exclusive ownership of the mutex. | |
112 | //!Effects: The calling thread releases the exclusive ownership of the mutex. | |
113 | //!Throws: An exception derived from interprocess_exception on error. | |
114 | void unlock(); | |
115 | ||
116 | //Sharable locking | |
117 | ||
1e59de90 TL |
118 | //!Requires: The calling thread does not own the mutex. |
119 | //! | |
7c673cae FG |
120 | //!Effects: The calling thread tries to obtain sharable ownership of the mutex, |
121 | //! and if another thread has exclusive ownership of the mutex, | |
122 | //! waits until it can obtain the ownership. | |
123 | //!Throws: interprocess_exception on error. | |
1e59de90 TL |
124 | //! |
125 | //!Note: A program may deadlock if the thread that has ownership calls | |
126 | //! this function. If the implementation can detect the deadlock, | |
127 | //! an exception could be thrown. | |
7c673cae FG |
128 | void lock_sharable(); |
129 | ||
1e59de90 TL |
130 | //!Same as `lock_sharable` but with a std-compatible interface |
131 | //! | |
132 | void lock_shared() | |
133 | { this->lock_sharable(); } | |
134 | ||
135 | //!Requires: The calling thread does not own the mutex. | |
136 | //! | |
7c673cae FG |
137 | //!Effects: The calling thread tries to acquire sharable ownership of the mutex |
138 | //! without waiting. If no other thread has exclusive ownership | |
139 | //! of the mutex this succeeds. | |
140 | //!Returns: If it can acquire sharable ownership immediately returns true. If it | |
141 | //! has to wait, returns false. | |
142 | //!Throws: interprocess_exception on error. | |
1e59de90 TL |
143 | //! |
144 | //!Note: A program may deadlock if the thread that has ownership calls | |
145 | //! this function. If the implementation can detect the deadlock, | |
146 | //! an exception could be thrown. | |
7c673cae FG |
147 | bool try_lock_sharable(); |
148 | ||
1e59de90 TL |
149 | //!Same as `try_lock_sharable` but with a std-compatible interface |
150 | //! | |
151 | bool try_lock_shared() | |
152 | { return this->try_lock_sharable(); } | |
153 | ||
154 | //!Requires: The calling thread does not own the mutex. | |
155 | //! | |
7c673cae FG |
156 | //!Effects: The calling thread tries to acquire sharable ownership of the mutex |
157 | //! waiting if necessary until no other thread has exclusive | |
158 | //! ownership of the mutex or abs_time is reached. | |
159 | //!Returns: If acquires sharable ownership, returns true. Otherwise returns false. | |
160 | //!Throws: interprocess_exception on error. | |
1e59de90 TL |
161 | //! |
162 | //!Note: A program may deadlock if the thread that has ownership calls | |
163 | //! this function. If the implementation can detect the deadlock, | |
164 | //! an exception could be thrown. | |
165 | template<class TimePoint> | |
166 | bool timed_lock_sharable(const TimePoint &abs_time); | |
167 | ||
168 | //!Same as `timed_lock_sharable`, but this function is modeled after the | |
169 | //!standard library interface. | |
170 | template<class TimePoint> bool try_lock_shared_until(const TimePoint &abs_time) | |
171 | { return this->timed_lock_sharable(abs_time); } | |
172 | ||
173 | //!Same as `timed_lock_sharable`, but this function is modeled after the | |
174 | //!standard library interface. | |
175 | template<class Duration> bool try_lock_shared_for(const Duration &dur) | |
176 | { return this->timed_lock_sharable(ipcdetail::duration_to_ustime(dur)); } | |
7c673cae FG |
177 | |
178 | //!Precondition: The thread must have sharable ownership of the mutex. | |
179 | //!Effects: The calling thread releases the sharable ownership of the mutex. | |
180 | //!Throws: An exception derived from interprocess_exception on error. | |
181 | void unlock_sharable(); | |
182 | ||
1e59de90 TL |
183 | //!Same as `unlock_sharable` but with a std-compatible interface |
184 | //! | |
185 | void unlock_shared() | |
186 | { this->unlock_sharable(); } | |
187 | ||
7c673cae FG |
188 | #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) |
189 | private: | |
190 | typedef scoped_lock<interprocess_mutex> scoped_lock_t; | |
191 | ||
192 | //Pack all the control data in a word to be able | |
193 | //to use atomic instructions in the future | |
194 | struct control_word_t | |
195 | { | |
196 | unsigned exclusive_in : 1; | |
197 | unsigned num_shared : sizeof(unsigned)*CHAR_BIT-1; | |
198 | } m_ctrl; | |
199 | ||
200 | interprocess_mutex m_mut; | |
201 | interprocess_condition m_first_gate; | |
202 | interprocess_condition m_second_gate; | |
203 | ||
204 | private: | |
205 | //Rollback structures for exceptions or failure return values | |
206 | struct exclusive_rollback | |
207 | { | |
208 | exclusive_rollback(control_word_t &ctrl | |
209 | ,interprocess_condition &first_gate) | |
210 | : mp_ctrl(&ctrl), m_first_gate(first_gate) | |
211 | {} | |
212 | ||
213 | void release() | |
214 | { mp_ctrl = 0; } | |
215 | ||
216 | ~exclusive_rollback() | |
217 | { | |
218 | if(mp_ctrl){ | |
219 | mp_ctrl->exclusive_in = 0; | |
220 | m_first_gate.notify_all(); | |
221 | } | |
222 | } | |
223 | control_word_t *mp_ctrl; | |
224 | interprocess_condition &m_first_gate; | |
225 | }; | |
226 | ||
227 | template<int Dummy> | |
228 | struct base_constants_t | |
229 | { | |
230 | static const unsigned max_readers | |
231 | = ~(unsigned(1) << (sizeof(unsigned)*CHAR_BIT-1)); | |
232 | }; | |
233 | typedef base_constants_t<0> constants; | |
234 | #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED | |
235 | }; | |
236 | ||
237 | #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) | |
238 | ||
239 | template <int Dummy> | |
240 | const unsigned interprocess_sharable_mutex::base_constants_t<Dummy>::max_readers; | |
241 | ||
242 | inline interprocess_sharable_mutex::interprocess_sharable_mutex() | |
243 | { | |
244 | this->m_ctrl.exclusive_in = 0; | |
245 | this->m_ctrl.num_shared = 0; | |
246 | } | |
247 | ||
248 | inline interprocess_sharable_mutex::~interprocess_sharable_mutex() | |
249 | {} | |
250 | ||
251 | inline void interprocess_sharable_mutex::lock() | |
252 | { | |
253 | scoped_lock_t lck(m_mut); | |
254 | ||
255 | //The exclusive lock must block in the first gate | |
256 | //if an exclusive lock has been acquired | |
257 | while (this->m_ctrl.exclusive_in){ | |
258 | this->m_first_gate.wait(lck); | |
259 | } | |
260 | ||
261 | //Mark that exclusive lock has been acquired | |
262 | this->m_ctrl.exclusive_in = 1; | |
263 | ||
264 | //Prepare rollback | |
265 | exclusive_rollback rollback(this->m_ctrl, this->m_first_gate); | |
266 | ||
267 | //Now wait until all readers are gone | |
268 | while (this->m_ctrl.num_shared){ | |
269 | this->m_second_gate.wait(lck); | |
270 | } | |
271 | rollback.release(); | |
272 | } | |
273 | ||
274 | inline bool interprocess_sharable_mutex::try_lock() | |
275 | { | |
276 | scoped_lock_t lck(m_mut, try_to_lock); | |
277 | ||
278 | //If we can't lock or any has there is any exclusive | |
279 | //or sharable mark return false; | |
280 | if(!lck.owns() | |
281 | || this->m_ctrl.exclusive_in | |
282 | || this->m_ctrl.num_shared){ | |
283 | return false; | |
284 | } | |
285 | this->m_ctrl.exclusive_in = 1; | |
286 | return true; | |
287 | } | |
288 | ||
1e59de90 | 289 | template<class TimePoint> |
7c673cae | 290 | inline bool interprocess_sharable_mutex::timed_lock |
1e59de90 | 291 | (const TimePoint &abs_time) |
7c673cae FG |
292 | { |
293 | scoped_lock_t lck(m_mut, abs_time); | |
294 | if(!lck.owns()) return false; | |
295 | ||
296 | //The exclusive lock must block in the first gate | |
297 | //if an exclusive lock has been acquired | |
298 | while (this->m_ctrl.exclusive_in){ | |
299 | //Mutexes and condvars handle just fine infinite abs_times | |
300 | //so avoid checking it here | |
301 | if(!this->m_first_gate.timed_wait(lck, abs_time)){ | |
302 | if(this->m_ctrl.exclusive_in){ | |
303 | return false; | |
304 | } | |
305 | break; | |
306 | } | |
307 | } | |
308 | ||
309 | //Mark that exclusive lock has been acquired | |
310 | this->m_ctrl.exclusive_in = 1; | |
311 | ||
312 | //Prepare rollback | |
313 | exclusive_rollback rollback(this->m_ctrl, this->m_first_gate); | |
314 | ||
315 | //Now wait until all readers are gone | |
316 | while (this->m_ctrl.num_shared){ | |
317 | //Mutexes and condvars handle just fine infinite abs_times | |
318 | //so avoid checking it here | |
319 | if(!this->m_second_gate.timed_wait(lck, abs_time)){ | |
320 | if(this->m_ctrl.num_shared){ | |
321 | return false; | |
322 | } | |
323 | break; | |
324 | } | |
325 | } | |
326 | rollback.release(); | |
327 | return true; | |
328 | } | |
329 | ||
330 | inline void interprocess_sharable_mutex::unlock() | |
331 | { | |
332 | scoped_lock_t lck(m_mut); | |
333 | this->m_ctrl.exclusive_in = 0; | |
334 | this->m_first_gate.notify_all(); | |
335 | } | |
336 | ||
337 | //Sharable locking | |
338 | ||
339 | inline void interprocess_sharable_mutex::lock_sharable() | |
340 | { | |
341 | scoped_lock_t lck(m_mut); | |
342 | ||
343 | //The sharable lock must block in the first gate | |
344 | //if an exclusive lock has been acquired | |
345 | //or there are too many sharable locks | |
346 | while(this->m_ctrl.exclusive_in | |
347 | || this->m_ctrl.num_shared == constants::max_readers){ | |
348 | this->m_first_gate.wait(lck); | |
349 | } | |
350 | ||
351 | //Increment sharable count | |
352 | ++this->m_ctrl.num_shared; | |
353 | } | |
354 | ||
355 | inline bool interprocess_sharable_mutex::try_lock_sharable() | |
356 | { | |
357 | scoped_lock_t lck(m_mut, try_to_lock); | |
358 | ||
359 | //The sharable lock must fail | |
360 | //if an exclusive lock has been acquired | |
361 | //or there are too many sharable locks | |
362 | if(!lck.owns() | |
363 | || this->m_ctrl.exclusive_in | |
364 | || this->m_ctrl.num_shared == constants::max_readers){ | |
365 | return false; | |
366 | } | |
367 | ||
368 | //Increment sharable count | |
369 | ++this->m_ctrl.num_shared; | |
370 | return true; | |
371 | } | |
372 | ||
1e59de90 | 373 | template<class TimePoint> |
7c673cae | 374 | inline bool interprocess_sharable_mutex::timed_lock_sharable |
1e59de90 | 375 | (const TimePoint &abs_time) |
7c673cae FG |
376 | { |
377 | scoped_lock_t lck(m_mut, abs_time); | |
378 | if(!lck.owns()) return false; | |
379 | ||
380 | //The sharable lock must block in the first gate | |
381 | //if an exclusive lock has been acquired | |
382 | //or there are too many sharable locks | |
383 | while (this->m_ctrl.exclusive_in | |
384 | || this->m_ctrl.num_shared == constants::max_readers){ | |
385 | //Mutexes and condvars handle just fine infinite abs_times | |
386 | //so avoid checking it here | |
387 | if(!this->m_first_gate.timed_wait(lck, abs_time)){ | |
388 | if(this->m_ctrl.exclusive_in | |
389 | || this->m_ctrl.num_shared == constants::max_readers){ | |
390 | return false; | |
391 | } | |
392 | break; | |
393 | } | |
394 | } | |
395 | ||
396 | //Increment sharable count | |
397 | ++this->m_ctrl.num_shared; | |
398 | return true; | |
399 | } | |
400 | ||
401 | inline void interprocess_sharable_mutex::unlock_sharable() | |
402 | { | |
403 | scoped_lock_t lck(m_mut); | |
404 | //Decrement sharable count | |
405 | --this->m_ctrl.num_shared; | |
406 | if (this->m_ctrl.num_shared == 0){ | |
407 | this->m_second_gate.notify_one(); | |
408 | } | |
409 | //Check if there are blocked sharables because of | |
410 | //there were too many sharables | |
411 | else if(this->m_ctrl.num_shared == (constants::max_readers-1)){ | |
412 | this->m_first_gate.notify_all(); | |
413 | } | |
414 | } | |
415 | ||
416 | #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED | |
417 | ||
418 | } //namespace interprocess { | |
419 | } //namespace boost { | |
420 | ||
421 | #include <boost/interprocess/detail/config_end.hpp> | |
422 | ||
423 | #endif //BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP |