]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | / Copyright (c) 2013 Vicente J. Botet Escriba | |
3 | / | |
4 | / Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | /] | |
7 | ||
8 | [section:synchronized_value_ref Reference ] | |
9 | ||
10 | ||
11 | #include <boost/thread/synchronized_value.hpp> | |
12 | namespace boost | |
13 | { | |
14 | ||
15 | template<typename T, typename Lockable = mutex> | |
16 | class synchronized_value; | |
17 | ||
18 | // Specialized swap algorithm | |
19 | template <typename T, typename L> | |
20 | void swap(synchronized_value<T,L> & lhs, synchronized_value<T,L> & rhs); | |
21 | template <typename T, typename L> | |
22 | void swap(synchronized_value<T,L> & lhs, T & rhs); | |
23 | template <typename T, typename L> | |
24 | void swap(T & lhs, synchronized_value<T,L> & rhs); | |
25 | ||
26 | // Hash support | |
27 | template<typename T, typename L> | |
28 | struct hash<synchronized_value<T,L> >; | |
29 | ||
30 | // Comparison | |
31 | template <typename T, typename L> | |
32 | bool operator==(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs) | |
33 | template <typename T, typename L> | |
34 | bool operator!=(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs) | |
35 | template <typename T, typename L> | |
36 | bool operator<(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs) | |
37 | template <typename T, typename L> | |
38 | bool operator<=(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs) | |
39 | template <typename T, typename L> | |
40 | bool operator>(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs) | |
41 | template <typename T, typename L> | |
42 | bool operator>=(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs) | |
43 | ||
44 | // Comparison with T | |
45 | template <typename T, typename L> | |
46 | bool operator==(T const& lhs, synchronized_value<T,L> const&rhs); | |
47 | template <typename T, typename L> | |
48 | bool operator!=(T const& lhs, synchronized_value<T,L> const&rhs); | |
49 | template <typename T, typename L> | |
50 | bool operator<(T const& lhs, synchronized_value<T,L> const&rhs); | |
51 | template <typename T, typename L> | |
52 | bool operator<=(T const& lhs, synchronized_value<T,L> const&rhs); | |
53 | template <typename T, typename L> | |
54 | bool operator>(T const& lhs, synchronized_value<T,L> const&rhs); | |
55 | template <typename T, typename L> | |
56 | bool operator>=(T const& lhs, synchronized_value<T,L> const&rhs); | |
57 | ||
58 | template <typename T, typename L> | |
59 | bool operator==(synchronized_value<T,L> const& lhs, T const& rhs); | |
60 | template <typename T, typename L> | |
61 | bool operator!=(synchronized_value<T,L> const& lhs, T const& rhs); | |
62 | template <typename T, typename L> | |
63 | bool operator<(synchronized_value<T,L> const& lhs, T const& rhs); | |
64 | template <typename T, typename L> | |
65 | bool operator<=(synchronized_value<T,L> const& lhs, T const& rhs); | |
66 | template <typename T, typename L> | |
67 | bool operator>(synchronized_value<T,L> const& lhs, T const& rhs); | |
68 | template <typename T, typename L> | |
69 | bool operator>=(synchronized_value<T,L> const& lhs, T const& rhs); | |
70 | ||
71 | #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) | |
72 | template <typename ...SV> | |
73 | std::tuple<typename synchronized_value_strict_lock_ptr<SV>::type ...> synchronize(SV& ...sv); | |
74 | #endif | |
75 | } | |
76 | ||
77 | [section:synchronized_value Class `synchronized_value`] | |
78 | ||
79 | #include <boost/thread/synchronized_value.hpp> | |
80 | ||
81 | namespace boost | |
82 | { | |
83 | ||
84 | template<typename T, typename Lockable = mutex> | |
85 | class synchronized_value | |
86 | { | |
87 | public: | |
88 | typedef T value_type; | |
89 | typedef Lockable mutex_type; | |
90 | ||
91 | synchronized_value() noexcept(is_nothrow_default_constructible<T>::value); | |
92 | synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value); | |
93 | synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value); | |
94 | synchronized_value(synchronized_value const& rhs); | |
95 | synchronized_value(synchronized_value&& other); | |
96 | ||
97 | // mutation | |
98 | synchronized_value& operator=(synchronized_value const& rhs); | |
99 | synchronized_value& operator=(value_type const& val); | |
100 | void swap(synchronized_value & rhs); | |
101 | void swap(value_type & rhs); | |
102 | ||
103 | //observers | |
104 | T get() const; | |
105 | #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) | |
106 | explicit operator T() const; | |
107 | #endif | |
108 | ||
109 | strict_lock_ptr<T,Lockable> operator->(); | |
110 | const_strict_lock_ptr<T,Lockable> operator->() const; | |
111 | strict_lock_ptr<T,Lockable> synchronize(); | |
112 | const_strict_lock_ptr<T,Lockable> synchronize() const; | |
113 | ||
114 | deref_value operator*();; | |
115 | const_deref_value operator*() const; | |
116 | ||
117 | private: | |
118 | T value_; // for exposition only | |
119 | mutable mutex_type mtx_; // for exposition only | |
120 | }; | |
121 | } | |
122 | ||
123 | [variablelist | |
124 | ||
125 | [[Requires:] [`Lockable` is `Lockable`.]] | |
126 | ||
127 | ] | |
128 | ||
129 | ||
130 | [section:constructor `synchronized_value()`] | |
131 | ||
132 | synchronized_value() noexcept(is_nothrow_default_constructible<T>::value); | |
133 | ||
134 | [variablelist | |
135 | ||
136 | [[Requires:] [`T` is `DefaultConstructible`.]] | |
137 | [[Effects:] [Default constructs the cloaked value_type]] | |
138 | ||
139 | [[Throws:] [Any exception thrown by `value_type()`.]] | |
140 | ||
141 | ] | |
142 | ||
143 | [endsect] | |
144 | ||
145 | ||
146 | [section:constructor_vt `synchronized_value(T const&)`] | |
147 | ||
148 | synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value); | |
149 | ||
150 | [variablelist | |
151 | ||
152 | [[Requires:] [`T` is `CopyConstructible`.]] | |
153 | [[Effects:] [Copy constructs the cloaked value_type using the parameter `other`]] | |
154 | ||
155 | [[Throws:] [Any exception thrown by `value_type(other)`.]] | |
156 | ||
157 | ] | |
158 | ||
159 | [endsect] | |
160 | ||
161 | [section:copy_cons `synchronized_value(synchronized_value const&)`] | |
162 | ||
163 | synchronized_value(synchronized_value const& rhs); | |
164 | ||
165 | [variablelist | |
166 | ||
167 | [[Requires:] [`T` is `DefaultConstructible` and `Assignable`.]] | |
168 | [[Effects:] [Assigns the value on a scope protected by the mutex of the rhs. The mutex is not copied.]] | |
169 | ||
170 | [[Throws:] [Any exception thrown by `value_type()` or `value_type& operator=(value_type&)` or `mtx_.lock()`.]] | |
171 | ||
172 | ] | |
173 | ||
174 | [endsect] | |
175 | ||
176 | [section:move_vt `synchronized_value(T&&)`] | |
177 | ||
178 | synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value); | |
179 | ||
180 | [variablelist | |
181 | ||
182 | [[Requires:] [`T` is `MoveConstructible `.]] | |
183 | [[Effects:] [Move constructs the cloaked value_type]] | |
184 | ||
185 | [[Throws:] [Any exception thrown by `value_type(value_type&&)`.]] | |
186 | ||
187 | ] | |
188 | ||
189 | [endsect] | |
190 | ||
191 | [section:move `synchronized_value(synchronized_value&&)`] | |
192 | ||
193 | synchronized_value(synchronized_value&& other); | |
194 | ||
195 | [variablelist | |
196 | ||
197 | [[Requires:] [`T` is `MoveConstructible `.]] | |
198 | [[Effects:] [Move constructs the cloaked value_type]] | |
199 | ||
200 | [[Throws:] [Any exception thrown by `value_type(value_type&&)` or `mtx_.lock()`.]] | |
201 | ||
202 | ] | |
203 | ||
204 | [endsect] | |
205 | ||
206 | [section:assign `operator=(synchronized_value const&)`] | |
207 | ||
208 | synchronized_value& operator=(synchronized_value const& rhs); | |
209 | ||
210 | [variablelist | |
211 | ||
212 | [[Requires:] [`T` is `Assignable`.]] | |
213 | [[Effects:] [Copies the underlying value on a scope protected by the two mutexes. The mutex is not copied. The locks are acquired avoiding deadlock. For example, there is no problem if one thread assigns `a = b` and the other assigns `b = a`.]] | |
214 | [[Return:] [`*this`]] | |
215 | ||
216 | [[Throws:] [Any exception thrown by `value_type& operator(value_type const&)` or `mtx_.lock()`.]] | |
217 | ||
218 | ] | |
219 | ||
220 | [endsect] | |
221 | [section:assign_vt `operator=(T const&)`] | |
222 | ||
223 | synchronized_value& operator=(value_type const& val); | |
224 | ||
225 | [variablelist | |
226 | ||
227 | [[Requires:] [`T` is `Assignable`.]] | |
228 | [[Effects:] [Copies the value on a scope protected by the mutex.]] | |
229 | [[Return:] [`*this`]] | |
230 | ||
231 | [[Throws:] [Any exception thrown by `value_type& operator(value_type const&)` or `mtx_.lock()`.]] | |
232 | ||
233 | ] | |
234 | ||
235 | [endsect] | |
236 | ||
237 | [section:get `get() const`] | |
238 | ||
239 | T get() const; | |
240 | ||
241 | [variablelist | |
242 | ||
243 | [[Requires:] [`T` is `CopyConstructible`.]] | |
244 | [[Return:] [`A copy of the protected value obtained on a scope protected by the mutex.`]] | |
245 | ||
246 | [[Throws:] [Any exception thrown by `value_type(value_type const&)` or `mtx_.lock()`.]] | |
247 | ||
248 | ] | |
249 | ||
250 | [endsect] | |
251 | ||
252 | ||
253 | [section:T `operator T() const`] | |
254 | ||
255 | #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) | |
256 | explicit operator T() const; | |
257 | #endif | |
258 | ||
259 | [variablelist | |
260 | ||
261 | [[Requires:] [`T` is `CopyConstructible`.]] | |
262 | [[Return:] [`A copy of the protected value obtained on a scope protected by the mutex.`]] | |
263 | ||
264 | [[Throws:] [Any exception thrown by `value_type(value_type const&)` or `mtx_.lock()`.]] | |
265 | ||
266 | ] | |
267 | ||
268 | [endsect] | |
269 | ||
270 | [section:swap `swap(synchronized_value&)`] | |
271 | ||
272 | void swap(synchronized_value & rhs); | |
273 | ||
274 | [variablelist | |
275 | ||
276 | [[Requires:] [`T` is `Assignable`.]] | |
277 | [[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]] | |
278 | ||
279 | [[Throws:] [Any exception thrown by `swap(value_, rhs.value)` or `mtx_.lock()` or `rhs_.mtx_.lock()`.]] | |
280 | ||
281 | ] | |
282 | ||
283 | [endsect] | |
284 | ||
285 | [section:swap_vt `swap(synchronized_value&)`] | |
286 | ||
287 | void swap(value_type & rhs); | |
288 | ||
289 | [variablelist | |
290 | ||
291 | [[Requires:] [`T` is `Swapable`.]] | |
292 | [[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]] | |
293 | ||
294 | [[Throws:] [Any exception thrown by `swap(value_, rhs)` or `mtx_.lock()`.]] | |
295 | ||
296 | ] | |
297 | ||
298 | [endsect] | |
299 | [section:indir `operator->()`] | |
300 | ||
301 | strict_lock_ptr<T,Lockable> operator->(); | |
302 | ||
303 | ||
304 | Essentially calling a method `obj->foo(x, y, z)` calls the method `foo(x, y, z)` inside a critical section as long-lived as the call itself. | |
305 | ||
306 | [variablelist | |
307 | ||
308 | [[Return:] [`A strict_lock_ptr<>.`]] | |
309 | ||
310 | [[Throws:] [Nothing.]] | |
311 | ||
312 | ] | |
313 | ||
314 | [endsect] | |
315 | [section:indir_const `operator->() const`] | |
316 | ||
317 | const_strict_lock_ptr<T,Lockable> operator->() const; | |
318 | ||
319 | ||
320 | If the `synchronized_value` object involved is const-qualified, then you'll only be able to call const methods | |
321 | through `operator->`. So, for example, `vec->push_back("xyz")` won't work if `vec` were const-qualified. | |
322 | The locking mechanism capitalizes on the assumption that const methods don't modify their underlying data. | |
323 | ||
324 | [variablelist | |
325 | ||
326 | [[Return:] [`A const_strict_lock_ptr <>.`]] | |
327 | ||
328 | [[Throws:] [Nothing.]] | |
329 | ||
330 | ] | |
331 | ||
332 | [endsect] | |
333 | [section:synchronize `synchronize()`] | |
334 | ||
335 | strict_lock_ptr<T,Lockable> synchronize(); | |
336 | ||
337 | The synchronize() factory make easier to lock on a scope. As discussed, `operator->` can only lock over the duration of a call, so it is insufficient for complex operations. With `synchronize()` you get to lock the object in a scoped and to directly access the object inside that scope. | |
338 | ||
339 | [*Example:] | |
340 | ||
341 | void fun(synchronized_value<vector<int>> & vec) { | |
342 | auto vec2=vec.synchronize(); | |
343 | vec2.push_back(42); | |
344 | assert(vec2.back() == 42); | |
345 | } | |
346 | ||
347 | [variablelist | |
348 | ||
349 | [[Return:] [`A strict_lock_ptr <>.`]] | |
350 | ||
351 | [[Throws:] [Nothing.]] | |
352 | ||
353 | ] | |
354 | ||
355 | [endsect] | |
356 | [section:synchronize_const `synchronize() const`] | |
357 | ||
358 | const_strict_lock_ptr<T,Lockable> synchronize() const; | |
359 | ||
360 | [variablelist | |
361 | ||
362 | [[Return:] [`A const_strict_lock_ptr <>.`]] | |
363 | ||
364 | [[Throws:] [Nothing.]] | |
365 | ||
366 | ] | |
367 | ||
368 | [endsect] | |
369 | ||
370 | [section:deref `operator*()`] | |
371 | ||
372 | deref_value operator*();; | |
373 | ||
374 | [variablelist | |
375 | ||
376 | [[Return:] [`A an instance of a class that locks the mutex on construction and unlocks it on destruction and provides implicit conversion to a reference to the protected value.`]] | |
377 | ||
378 | [[Throws:] [Nothing.]] | |
379 | ||
380 | ] | |
381 | ||
382 | [endsect] | |
383 | [section:deref_const `operator*() const`] | |
384 | ||
385 | const_deref_value operator*() const; | |
386 | ||
387 | ||
388 | [variablelist | |
389 | ||
390 | [[Return:] [`A an instance of a class that locks the mutex on construction and unlocks it on destruction and provides implicit conversion to a constant reference to the protected value.`]] | |
391 | ||
392 | [[Throws:] [Nothing.]] | |
393 | ||
394 | ] | |
395 | ||
396 | [endsect] | |
397 | ||
398 | ||
399 | ||
400 | ||
401 | [endsect] | |
402 | [section:synchronize Non-Member Function `synchronize`] | |
403 | ||
404 | #include <boost/thread/synchronized_value.hpp> | |
405 | namespace boost | |
406 | { | |
407 | #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) | |
408 | template <typename ...SV> | |
409 | std::tuple<typename synchronized_value_strict_lock_ptr<SV>::type ...> synchronize(SV& ...sv); | |
410 | #endif | |
411 | } | |
412 | ||
413 | [endsect] | |
414 | [endsect] | |
415 |