1 // (C) Copyright 2010 Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 2 // (C) Copyright 2012 Vicente J. Botet Escriba 3 // Distributed under the Boost Software License, Version 1.0. (See 4 // accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 8 #ifndef BOOST_THREAD_SYNCHRONIZED_VALUE_HPP 9 #define BOOST_THREAD_SYNCHRONIZED_VALUE_HPP 10 11 #include <boost/thread/detail/config.hpp> 12 13 #include <boost/thread/detail/move.hpp> 14 #include <boost/thread/mutex.hpp> 15 #include <boost/thread/lock_types.hpp> 16 #include <boost/thread/lock_guard.hpp> 17 #include <boost/thread/lock_algorithms.hpp> 18 #include <boost/thread/lock_factories.hpp> 19 #include <boost/thread/strict_lock.hpp> 20 #include <boost/core/swap.hpp> 21 #include <boost/utility/declval.hpp> 22 //#include <boost/type_traits.hpp> 23 //#include <boost/thread/detail/is_nothrow_default_constructible.hpp> 24 //#if ! defined BOOST_NO_CXX11_HDR_TYPE_TRAITS 25 //#include <type_traits> 26 //#endif 27 28 #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) 29 #include <tuple> // todo change to <boost/tuple.hpp> once Boost.Tuple or Boost.Fusion provides Move semantics on C++98 compilers. 30 #include <functional> 31 #endif 32 33 #include <boost/utility/result_of.hpp> 34 35 #include <boost/config/abi_prefix.hpp> 36 37 namespace boost 38 { 39 40 /** 41 * strict lock providing a const pointer access to the synchronized value type. 42 * 43 * @param T the value type. 44 * @param Lockable the mutex type protecting the value type. 45 */ 46 template <typename T, typename Lockable = mutex> 47 class const_strict_lock_ptr 48 { 49 public: 50 typedef T value_type; 51 typedef Lockable mutex_type; 52 protected: 53 54 // this should be a strict_lock, but unique_lock is needed to be able to return it. 55 boost::unique_lock<mutex_type> lk_; 56 T const& value_; 57 58 public: 59 BOOST_THREAD_MOVABLE_ONLY( const_strict_lock_ptr ) 60 61 /** 62 * @param value constant reference of the value to protect. 63 * @param mtx reference to the mutex used to protect the value. 64 * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. 65 */ const_strict_lock_ptr(T const & val,Lockable & mtx)66 const_strict_lock_ptr(T const& val, Lockable & mtx) : 67 lk_(mtx), value_(val) 68 { 69 } const_strict_lock_ptr(T const & val,Lockable & mtx,adopt_lock_t tag)70 const_strict_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t tag) BOOST_NOEXCEPT : 71 lk_(mtx, tag), value_(val) 72 { 73 } 74 /** 75 * Move constructor. 76 * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. 77 */ const_strict_lock_ptr(BOOST_THREAD_RV_REF (const_strict_lock_ptr)other)78 const_strict_lock_ptr(BOOST_THREAD_RV_REF(const_strict_lock_ptr) other) BOOST_NOEXCEPT 79 : lk_(boost::move(BOOST_THREAD_RV(other).lk_)),value_(BOOST_THREAD_RV(other).value_) 80 { 81 } 82 ~const_strict_lock_ptr()83 ~const_strict_lock_ptr() 84 { 85 } 86 87 /** 88 * @return a constant pointer to the protected value 89 */ operator ->() const90 const T* operator->() const 91 { 92 return &value_; 93 } 94 95 /** 96 * @return a constant reference to the protected value 97 */ operator *() const98 const T& operator*() const 99 { 100 return value_; 101 } 102 103 }; 104 105 /** 106 * strict lock providing a pointer access to the synchronized value type. 107 * 108 * @param T the value type. 109 * @param Lockable the mutex type protecting the value type. 110 */ 111 template <typename T, typename Lockable = mutex> 112 class strict_lock_ptr : public const_strict_lock_ptr<T,Lockable> 113 { 114 typedef const_strict_lock_ptr<T,Lockable> base_type; 115 public: 116 BOOST_THREAD_MOVABLE_ONLY( strict_lock_ptr ) 117 118 /** 119 * @param value reference of the value to protect. 120 * @param mtx reference to the mutex used to protect the value. 121 * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. 122 */ strict_lock_ptr(T & val,Lockable & mtx)123 strict_lock_ptr(T & val, Lockable & mtx) : 124 base_type(val, mtx) 125 { 126 } strict_lock_ptr(T & val,Lockable & mtx,adopt_lock_t tag)127 strict_lock_ptr(T & val, Lockable & mtx, adopt_lock_t tag) : 128 base_type(val, mtx, tag) 129 { 130 } 131 132 /** 133 * Move constructor. 134 * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. 135 */ strict_lock_ptr(BOOST_THREAD_RV_REF (strict_lock_ptr)other)136 strict_lock_ptr(BOOST_THREAD_RV_REF(strict_lock_ptr) other) 137 : base_type(boost::move(static_cast<base_type&>(other))) 138 { 139 } 140 ~strict_lock_ptr()141 ~strict_lock_ptr() 142 { 143 } 144 145 /** 146 * @return a pointer to the protected value 147 */ operator ->()148 T* operator->() 149 { 150 return const_cast<T*>(&this->value_); 151 } 152 153 /** 154 * @return a reference to the protected value 155 */ operator *()156 T& operator*() 157 { 158 return const_cast<T&>(this->value_); 159 } 160 161 }; 162 163 template <typename SV> 164 struct synchronized_value_strict_lock_ptr 165 { 166 typedef strict_lock_ptr<typename SV::value_type, typename SV::mutex_type> type; 167 }; 168 169 template <typename SV> 170 struct synchronized_value_strict_lock_ptr<const SV> 171 { 172 typedef const_strict_lock_ptr<typename SV::value_type, typename SV::mutex_type> type; 173 }; 174 /** 175 * unique_lock providing a const pointer access to the synchronized value type. 176 * 177 * An object of type const_unique_lock_ptr is a unique_lock that provides a const pointer access to the synchronized value type. 178 * As unique_lock controls the ownership of a lockable object within a scope. 179 * Ownership of the lockable object may be acquired at construction or after construction, 180 * and may be transferred, after acquisition, to another const_unique_lock_ptr object. 181 * Objects of type const_unique_lock_ptr are not copyable but are movable. 182 * The behavior of a program is undefined if the mutex and the value type 183 * pointed do not exist for the entire remaining lifetime of the const_unique_lock_ptr object. 184 * The supplied Mutex type shall meet the BasicLockable requirements. 185 * 186 * @note const_unique_lock_ptr<T, Lockable> meets the Lockable requirements. 187 * If Lockable meets the TimedLockable requirements, const_unique_lock_ptr<T,Lockable> 188 * also meets the TimedLockable requirements. 189 * 190 * @param T the value type. 191 * @param Lockable the mutex type protecting the value type. 192 */ 193 template <typename T, typename Lockable = mutex> 194 class const_unique_lock_ptr : public unique_lock<Lockable> 195 { 196 typedef unique_lock<Lockable> base_type; 197 public: 198 typedef T value_type; 199 typedef Lockable mutex_type; 200 protected: 201 T const& value_; 202 203 public: 204 BOOST_THREAD_MOVABLE_ONLY(const_unique_lock_ptr) 205 206 /** 207 * @param value reference of the value to protect. 208 * @param mtx reference to the mutex used to protect the value. 209 * 210 * @requires If mutex_type is not a recursive mutex the calling thread does not own the mutex. 211 * 212 * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. 213 */ const_unique_lock_ptr(T const & val,Lockable & mtx)214 const_unique_lock_ptr(T const& val, Lockable & mtx) 215 : base_type(mtx), value_(val) 216 { 217 } 218 /** 219 * @param value reference of the value to protect. 220 * @param mtx reference to the mutex used to protect the value. 221 * @param tag of type adopt_lock_t used to differentiate the constructor. 222 * @requires The calling thread own the mutex. 223 * @effects stores a reference to it and to the value type @c value taking ownership. 224 */ const_unique_lock_ptr(T const & val,Lockable & mtx,adopt_lock_t)225 const_unique_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t) BOOST_NOEXCEPT 226 : base_type(mtx, adopt_lock), value_(val) 227 { 228 } 229 /** 230 * @param value reference of the value to protect. 231 * @param mtx reference to the mutex used to protect the value. 232 * @param tag of type defer_lock_t used to differentiate the constructor. 233 * @effects stores a reference to it and to the value type @c value c. 234 */ const_unique_lock_ptr(T const & val,Lockable & mtx,defer_lock_t)235 const_unique_lock_ptr(T const& val, Lockable & mtx, defer_lock_t) BOOST_NOEXCEPT 236 : base_type(mtx, defer_lock), value_(val) 237 { 238 } 239 /** 240 * @param value reference of the value to protect. 241 * @param mtx reference to the mutex used to protect the value. 242 * @param tag of type try_to_lock_t used to differentiate the constructor. 243 * @requires If mutex_type is not a recursive mutex the calling thread does not own the mutex. 244 * @effects try to lock the mutex @c mtx, stores a reference to it and to the value type @c value. 245 */ const_unique_lock_ptr(T const & val,Lockable & mtx,try_to_lock_t)246 const_unique_lock_ptr(T const& val, Lockable & mtx, try_to_lock_t) BOOST_NOEXCEPT 247 : base_type(mtx, try_to_lock), value_(val) 248 { 249 } 250 /** 251 * Move constructor. 252 * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. 253 */ const_unique_lock_ptr(BOOST_THREAD_RV_REF (const_unique_lock_ptr)other)254 const_unique_lock_ptr(BOOST_THREAD_RV_REF(const_unique_lock_ptr) other) BOOST_NOEXCEPT 255 : base_type(boost::move(static_cast<base_type&>(other))), value_(BOOST_THREAD_RV(other).value_) 256 { 257 } 258 259 /** 260 * @effects If owns calls unlock() on the owned mutex. 261 */ ~const_unique_lock_ptr()262 ~const_unique_lock_ptr() 263 { 264 } 265 266 /** 267 * @return a constant pointer to the protected value 268 */ operator ->() const269 const T* operator->() const 270 { 271 BOOST_ASSERT (this->owns_lock()); 272 return &value_; 273 } 274 275 /** 276 * @return a constant reference to the protected value 277 */ operator *() const278 const T& operator*() const 279 { 280 BOOST_ASSERT (this->owns_lock()); 281 return value_; 282 } 283 284 }; 285 286 /** 287 * unique lock providing a pointer access to the synchronized value type. 288 * 289 * @param T the value type. 290 * @param Lockable the mutex type protecting the value type. 291 */ 292 template <typename T, typename Lockable = mutex> 293 class unique_lock_ptr : public const_unique_lock_ptr<T, Lockable> 294 { 295 typedef const_unique_lock_ptr<T, Lockable> base_type; 296 public: 297 typedef T value_type; 298 typedef Lockable mutex_type; 299 300 BOOST_THREAD_MOVABLE_ONLY(unique_lock_ptr) 301 302 /** 303 * @param value reference of the value to protect. 304 * @param mtx reference to the mutex used to protect the value. 305 * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. 306 */ unique_lock_ptr(T & val,Lockable & mtx)307 unique_lock_ptr(T & val, Lockable & mtx) 308 : base_type(val, mtx) 309 { 310 } 311 /** 312 * @param value reference of the value to protect. 313 * @param mtx reference to the mutex used to protect the value. 314 * @param tag of type adopt_lock_t used to differentiate the constructor. 315 * @effects stores a reference to it and to the value type @c value taking ownership. 316 */ unique_lock_ptr(T & value,Lockable & mtx,adopt_lock_t)317 unique_lock_ptr(T & value, Lockable & mtx, adopt_lock_t) BOOST_NOEXCEPT 318 : base_type(value, mtx, adopt_lock) 319 { 320 } 321 /** 322 * @param value reference of the value to protect. 323 * @param mtx reference to the mutex used to protect the value. 324 * @param tag of type defer_lock_t used to differentiate the constructor. 325 * @effects stores a reference to it and to the value type @c value c. 326 */ unique_lock_ptr(T & value,Lockable & mtx,defer_lock_t)327 unique_lock_ptr(T & value, Lockable & mtx, defer_lock_t) BOOST_NOEXCEPT 328 : base_type(value, mtx, defer_lock) 329 { 330 } 331 /** 332 * @param value reference of the value to protect. 333 * @param mtx reference to the mutex used to protect the value. 334 * @param tag of type try_to_lock_t used to differentiate the constructor. 335 * @effects try to lock the mutex @c mtx, stores a reference to it and to the value type @c value. 336 */ unique_lock_ptr(T & value,Lockable & mtx,try_to_lock_t)337 unique_lock_ptr(T & value, Lockable & mtx, try_to_lock_t) BOOST_NOEXCEPT 338 : base_type(value, mtx, try_to_lock) 339 { 340 } 341 /** 342 * Move constructor. 343 * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. 344 */ unique_lock_ptr(BOOST_THREAD_RV_REF (unique_lock_ptr)other)345 unique_lock_ptr(BOOST_THREAD_RV_REF(unique_lock_ptr) other) BOOST_NOEXCEPT 346 : base_type(boost::move(static_cast<base_type&>(other))) 347 { 348 } 349 ~unique_lock_ptr()350 ~unique_lock_ptr() 351 { 352 } 353 354 /** 355 * @return a pointer to the protected value 356 */ operator ->()357 T* operator->() 358 { 359 BOOST_ASSERT (this->owns_lock()); 360 return const_cast<T*>(&this->value_); 361 } 362 363 /** 364 * @return a reference to the protected value 365 */ operator *()366 T& operator*() 367 { 368 BOOST_ASSERT (this->owns_lock()); 369 return const_cast<T&>(this->value_); 370 } 371 372 373 }; 374 375 template <typename SV> 376 struct synchronized_value_unique_lock_ptr 377 { 378 typedef unique_lock_ptr<typename SV::value_type, typename SV::mutex_type> type; 379 }; 380 381 template <typename SV> 382 struct synchronized_value_unique_lock_ptr<const SV> 383 { 384 typedef const_unique_lock_ptr<typename SV::value_type, typename SV::mutex_type> type; 385 }; 386 /** 387 * cloaks a value type and the mutex used to protect it together. 388 * @param T the value type. 389 * @param Lockable the mutex type protecting the value type. 390 */ 391 template <typename T, typename Lockable = mutex> 392 class synchronized_value 393 { 394 395 #if ! defined(BOOST_THREAD_NO_MAKE_UNIQUE_LOCKS) 396 #if ! defined BOOST_NO_CXX11_VARIADIC_TEMPLATES 397 template <typename ...SV> 398 friend std::tuple<typename synchronized_value_strict_lock_ptr<SV>::type ...> synchronize(SV& ...sv); 399 #else 400 template <typename SV1, typename SV2> 401 friend std::tuple< 402 typename synchronized_value_strict_lock_ptr<SV1>::type, 403 typename synchronized_value_strict_lock_ptr<SV2>::type 404 > 405 synchronize(SV1& sv1, SV2& sv2); 406 template <typename SV1, typename SV2, typename SV3> 407 friend std::tuple< 408 typename synchronized_value_strict_lock_ptr<SV1>::type, 409 typename synchronized_value_strict_lock_ptr<SV2>::type, 410 typename synchronized_value_strict_lock_ptr<SV3>::type 411 > 412 synchronize(SV1& sv1, SV2& sv2, SV3& sv3); 413 #endif 414 #endif 415 416 public: 417 typedef T value_type; 418 typedef Lockable mutex_type; 419 private: 420 T value_; 421 mutable mutex_type mtx_; 422 public: 423 // construction/destruction 424 /** 425 * Default constructor. 426 * 427 * @Requires: T is DefaultConstructible 428 */ synchronized_value()429 synchronized_value() 430 //BOOST_NOEXCEPT_IF(is_nothrow_default_constructible<T>::value) 431 : value_() 432 { 433 } 434 435 /** 436 * Constructor from copy constructible value. 437 * 438 * Requires: T is CopyConstructible 439 */ synchronized_value(T const & other)440 synchronized_value(T const& other) 441 //BOOST_NOEXCEPT_IF(is_nothrow_copy_constructible<T>::value) 442 : value_(other) 443 { 444 } 445 446 /** 447 * Move Constructor. 448 * 449 * Requires: T is CopyMovable 450 */ synchronized_value(BOOST_THREAD_RV_REF (T)other)451 synchronized_value(BOOST_THREAD_RV_REF(T) other) 452 //BOOST_NOEXCEPT_IF(is_nothrow_move_constructible<T>::value) 453 : value_(boost::move(other)) 454 { 455 } 456 457 /** 458 * Constructor from value type. 459 * 460 * Requires: T is DefaultConstructible and Assignable 461 * Effects: Assigns the value on a scope protected by the mutex of the rhs. The mutex is not copied. 462 */ synchronized_value(synchronized_value const & rhs)463 synchronized_value(synchronized_value const& rhs) 464 { 465 strict_lock<mutex_type> lk(rhs.mtx_); 466 value_ = rhs.value_; 467 } 468 469 /** 470 * Move Constructor from movable value type 471 * 472 */ synchronized_value(BOOST_THREAD_RV_REF (synchronized_value)other)473 synchronized_value(BOOST_THREAD_RV_REF(synchronized_value) other) 474 { 475 strict_lock<mutex_type> lk(BOOST_THREAD_RV(other).mtx_); 476 value_= boost::move(BOOST_THREAD_RV(other).value_); 477 } 478 479 // mutation 480 /** 481 * Assignment operator. 482 * 483 * Effects: Copies the underlying value on a scope protected by the two mutexes. 484 * The mutex is not copied. The locks are acquired using lock, so deadlock is avoided. 485 * For example, there is no problem if one thread assigns a = b and the other assigns b = a. 486 * 487 * Return: *this 488 */ 489 operator =(synchronized_value const & rhs)490 synchronized_value& operator=(synchronized_value const& rhs) 491 { 492 if(&rhs != this) 493 { 494 // auto _ = make_unique_locks(mtx_, rhs.mtx_); 495 unique_lock<mutex_type> lk1(mtx_, defer_lock); 496 unique_lock<mutex_type> lk2(rhs.mtx_, defer_lock); 497 lock(lk1,lk2); 498 499 value_ = rhs.value_; 500 } 501 return *this; 502 } 503 /** 504 * Assignment operator from a T const&. 505 * Effects: The operator copies the value on a scope protected by the mutex. 506 * Return: *this 507 */ operator =(value_type const & val)508 synchronized_value& operator=(value_type const& val) 509 { 510 { 511 strict_lock<mutex_type> lk(mtx_); 512 value_ = val; 513 } 514 return *this; 515 } 516 517 //observers 518 /** 519 * Explicit conversion to value type. 520 * 521 * Requires: T is CopyConstructible 522 * Return: A copy of the protected value obtained on a scope protected by the mutex. 523 * 524 */ get() const525 T get() const 526 { 527 strict_lock<mutex_type> lk(mtx_); 528 return value_; 529 } 530 /** 531 * Explicit conversion to value type. 532 * 533 * Requires: T is CopyConstructible 534 * Return: A copy of the protected value obtained on a scope protected by the mutex. 535 * 536 */ 537 #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) operator T() const538 explicit operator T() const 539 { 540 return get(); 541 } 542 #endif 543 544 /** 545 * value type getter. 546 * 547 * Return: A constant reference to the protected value. 548 * 549 * Note: Not thread safe 550 * 551 */ value() const552 T const& value() const 553 { 554 return value_; 555 } 556 /** 557 * mutex getter. 558 * 559 * Return: A constant reference to the protecting mutex. 560 * 561 * Note: Not thread safe 562 * 563 */ mutex() const564 mutex_type const& mutex() const 565 { 566 return mtx_; 567 } 568 /** 569 * Swap 570 * 571 * Effects: Swaps the data. Again, locks are acquired using lock(). The mutexes are not swapped. 572 * A swap method accepts a T& and swaps the data inside a critical section. 573 * This is by far the preferred method of changing the guarded datum wholesale because it keeps the lock only 574 * for a short time, thus lowering the pressure on the mutex. 575 */ swap(synchronized_value & rhs)576 void swap(synchronized_value & rhs) 577 { 578 if (this == &rhs) { 579 return; 580 } 581 // auto _ = make_unique_locks(mtx_, rhs.mtx_); 582 unique_lock<mutex_type> lk1(mtx_, defer_lock); 583 unique_lock<mutex_type> lk2(rhs.mtx_, defer_lock); 584 lock(lk1,lk2); 585 boost::swap(value_, rhs.value_); 586 } 587 /** 588 * Swap with the underlying value type 589 * 590 * Effects: Swaps the data on a scope protected by the mutex. 591 */ swap(value_type & rhs)592 void swap(value_type & rhs) 593 { 594 strict_lock<mutex_type> lk(mtx_); 595 boost::swap(value_, rhs); 596 } 597 598 /** 599 * Essentially calling a method obj->foo(x, y, z) calls the method foo(x, y, z) inside a critical section as 600 * long-lived as the call itself. 601 */ operator ->()602 strict_lock_ptr<T,Lockable> operator->() 603 { 604 return BOOST_THREAD_MAKE_RV_REF((strict_lock_ptr<T,Lockable>(value_, mtx_))); 605 } 606 /** 607 * If the synchronized_value object involved is const-qualified, then you'll only be able to call const methods 608 * through operator->. So, for example, vec->push_back("xyz") won't work if vec were const-qualified. 609 * The locking mechanism capitalizes on the assumption that const methods don't modify their underlying data. 610 */ operator ->() const611 const_strict_lock_ptr<T,Lockable> operator->() const 612 { 613 return BOOST_THREAD_MAKE_RV_REF((const_strict_lock_ptr<T,Lockable>(value_, mtx_))); 614 } 615 616 /** 617 * Call function on a locked block. 618 * 619 * @requires fct(value_) is well formed. 620 * 621 * Example 622 * void fun(synchronized_value<vector<int>> & v) { 623 * v ( [](vector<int>> & vec) 624 * { 625 * vec.push_back(42); 626 * assert(vec.back() == 42); 627 * } ); 628 * } 629 */ 630 template <typename F> 631 inline 632 typename boost::result_of<F(value_type&)>::type operator ()(BOOST_THREAD_RV_REF (F)fct)633 operator()(BOOST_THREAD_RV_REF(F) fct) 634 { 635 strict_lock<mutex_type> lk(mtx_); 636 return fct(value_); 637 } 638 template <typename F> 639 inline 640 typename boost::result_of<F(value_type const&)>::type operator ()(BOOST_THREAD_RV_REF (F)fct) const641 operator()(BOOST_THREAD_RV_REF(F) fct) const 642 { 643 strict_lock<mutex_type> lk(mtx_); 644 return fct(value_); 645 } 646 647 648 #if defined BOOST_NO_CXX11_RVALUE_REFERENCES 649 template <typename F> 650 inline 651 typename boost::result_of<F(value_type&)>::type operator ()(F const & fct)652 operator()(F const & fct) 653 { 654 strict_lock<mutex_type> lk(mtx_); 655 return fct(value_); 656 } 657 template <typename F> 658 inline 659 typename boost::result_of<F(value_type const&)>::type operator ()(F const & fct) const660 operator()(F const & fct) const 661 { 662 strict_lock<mutex_type> lk(mtx_); 663 return fct(value_); 664 } 665 666 template <typename R> 667 inline operator ()(R (* fct)(value_type &))668 R operator()(R(*fct)(value_type&)) 669 { 670 strict_lock<mutex_type> lk(mtx_); 671 return fct(value_); 672 } 673 template <typename R> 674 inline operator ()(R (* fct)(value_type const &)) const675 R operator()(R(*fct)(value_type const&)) const 676 { 677 strict_lock<mutex_type> lk(mtx_); 678 return fct(value_); 679 } 680 #endif 681 682 683 /** 684 * The synchronize() factory make easier to lock on a scope. 685 * As discussed, operator-> can only lock over the duration of a call, so it is insufficient for complex operations. 686 * With synchronize() you get to lock the object in a scoped and to directly access the object inside that scope. 687 * 688 * Example 689 * void fun(synchronized_value<vector<int>> & v) { 690 * auto&& vec=v.synchronize(); 691 * vec.push_back(42); 692 * assert(vec.back() == 42); 693 * } 694 */ synchronize()695 strict_lock_ptr<T,Lockable> synchronize() 696 { 697 return BOOST_THREAD_MAKE_RV_REF((strict_lock_ptr<T,Lockable>(value_, mtx_))); 698 } synchronize() const699 const_strict_lock_ptr<T,Lockable> synchronize() const 700 { 701 return BOOST_THREAD_MAKE_RV_REF((const_strict_lock_ptr<T,Lockable>(value_, mtx_))); 702 } 703 unique_synchronize()704 unique_lock_ptr<T,Lockable> unique_synchronize() 705 { 706 return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr<T,Lockable>(value_, mtx_))); 707 } unique_synchronize() const708 const_unique_lock_ptr<T,Lockable> unique_synchronize() const 709 { 710 return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr<T,Lockable>(value_, mtx_))); 711 } unique_synchronize(defer_lock_t tag)712 unique_lock_ptr<T,Lockable> unique_synchronize(defer_lock_t tag) 713 { 714 return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr<T,Lockable>(value_, mtx_, tag))); 715 } unique_synchronize(defer_lock_t tag) const716 const_unique_lock_ptr<T,Lockable> unique_synchronize(defer_lock_t tag) const 717 { 718 return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr<T,Lockable>(value_, mtx_, tag))); 719 } defer_synchronize()720 unique_lock_ptr<T,Lockable> defer_synchronize() BOOST_NOEXCEPT 721 { 722 return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr<T,Lockable>(value_, mtx_, defer_lock))); 723 } defer_synchronize() const724 const_unique_lock_ptr<T,Lockable> defer_synchronize() const BOOST_NOEXCEPT 725 { 726 return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr<T,Lockable>(value_, mtx_, defer_lock))); 727 } try_to_synchronize()728 unique_lock_ptr<T,Lockable> try_to_synchronize() BOOST_NOEXCEPT 729 { 730 return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr<T,Lockable>(value_, mtx_, try_to_lock))); 731 } try_to_synchronize() const732 const_unique_lock_ptr<T,Lockable> try_to_synchronize() const BOOST_NOEXCEPT 733 { 734 return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr<T,Lockable>(value_, mtx_, try_to_lock))); 735 } adopt_synchronize()736 unique_lock_ptr<T,Lockable> adopt_synchronize() BOOST_NOEXCEPT 737 { 738 return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr<T,Lockable>(value_, mtx_, adopt_lock))); 739 } adopt_synchronize() const740 const_unique_lock_ptr<T,Lockable> adopt_synchronize() const BOOST_NOEXCEPT 741 { 742 return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr<T,Lockable>(value_, mtx_, adopt_lock))); 743 } 744 745 746 #if ! defined __IBMCPP__ 747 private: 748 #endif 749 class deref_value 750 { 751 private: 752 friend class synchronized_value; 753 754 boost::unique_lock<mutex_type> lk_; 755 T& value_; 756 deref_value(synchronized_value & outer)757 explicit deref_value(synchronized_value& outer): 758 lk_(outer.mtx_),value_(outer.value_) 759 {} 760 761 public: 762 BOOST_THREAD_MOVABLE_ONLY(deref_value) 763 deref_value(BOOST_THREAD_RV_REF (deref_value)other)764 deref_value(BOOST_THREAD_RV_REF(deref_value) other): 765 lk_(boost::move(BOOST_THREAD_RV(other).lk_)),value_(BOOST_THREAD_RV(other).value_) 766 {} operator T&()767 operator T&() 768 { 769 return value_; 770 } 771 operator =(T const & newVal)772 deref_value& operator=(T const& newVal) 773 { 774 value_=newVal; 775 return *this; 776 } 777 }; 778 class const_deref_value 779 { 780 private: 781 friend class synchronized_value; 782 783 boost::unique_lock<mutex_type> lk_; 784 const T& value_; 785 const_deref_value(synchronized_value const & outer)786 explicit const_deref_value(synchronized_value const& outer): 787 lk_(outer.mtx_), value_(outer.value_) 788 {} 789 790 public: 791 BOOST_THREAD_MOVABLE_ONLY(const_deref_value) 792 const_deref_value(BOOST_THREAD_RV_REF (const_deref_value)other)793 const_deref_value(BOOST_THREAD_RV_REF(const_deref_value) other): 794 lk_(boost::move(BOOST_THREAD_RV(other).lk_)), value_(BOOST_THREAD_RV(other).value_) 795 {} 796 operator const T&()797 operator const T&() 798 { 799 return value_; 800 } 801 }; 802 803 public: operator *()804 deref_value operator*() 805 { 806 return BOOST_THREAD_MAKE_RV_REF(deref_value(*this)); 807 } 808 operator *() const809 const_deref_value operator*() const 810 { 811 return BOOST_THREAD_MAKE_RV_REF(const_deref_value(*this)); 812 } 813 814 // io functions 815 /** 816 * @requires T is OutputStreamable 817 * @effects saves the value type on the output stream @c os. 818 */ 819 template <typename OStream> save(OStream & os) const820 void save(OStream& os) const 821 { 822 strict_lock<mutex_type> lk(mtx_); 823 os << value_; 824 } 825 /** 826 * @requires T is InputStreamable 827 * @effects loads the value type from the input stream @c is. 828 */ 829 template <typename IStream> load(IStream & is)830 void load(IStream& is) 831 { 832 strict_lock<mutex_type> lk(mtx_); 833 is >> value_; 834 } 835 836 // relational operators 837 /** 838 * @requires T is EqualityComparable 839 * 840 */ operator ==(synchronized_value const & rhs) const841 bool operator==(synchronized_value const& rhs) const 842 { 843 unique_lock<mutex_type> lk1(mtx_, defer_lock); 844 unique_lock<mutex_type> lk2(rhs.mtx_, defer_lock); 845 lock(lk1,lk2); 846 847 return value_ == rhs.value_; 848 } 849 /** 850 * @requires T is LessThanComparable 851 * 852 */ operator <(synchronized_value const & rhs) const853 bool operator<(synchronized_value const& rhs) const 854 { 855 unique_lock<mutex_type> lk1(mtx_, defer_lock); 856 unique_lock<mutex_type> lk2(rhs.mtx_, defer_lock); 857 lock(lk1,lk2); 858 859 return value_ < rhs.value_; 860 } 861 /** 862 * @requires T is GreaterThanComparable 863 * 864 */ operator >(synchronized_value const & rhs) const865 bool operator>(synchronized_value const& rhs) const 866 { 867 unique_lock<mutex_type> lk1(mtx_, defer_lock); 868 unique_lock<mutex_type> lk2(rhs.mtx_, defer_lock); 869 lock(lk1,lk2); 870 871 return value_ > rhs.value_; 872 } operator <=(synchronized_value const & rhs) const873 bool operator<=(synchronized_value const& rhs) const 874 { 875 unique_lock<mutex_type> lk1(mtx_, defer_lock); 876 unique_lock<mutex_type> lk2(rhs.mtx_, defer_lock); 877 lock(lk1,lk2); 878 879 return value_ <= rhs.value_; 880 } operator >=(synchronized_value const & rhs) const881 bool operator>=(synchronized_value const& rhs) const 882 { 883 unique_lock<mutex_type> lk1(mtx_, defer_lock); 884 unique_lock<mutex_type> lk2(rhs.mtx_, defer_lock); 885 lock(lk1,lk2); 886 887 return value_ >= rhs.value_; 888 } operator ==(value_type const & rhs) const889 bool operator==(value_type const& rhs) const 890 { 891 unique_lock<mutex_type> lk1(mtx_); 892 893 return value_ == rhs; 894 } operator !=(value_type const & rhs) const895 bool operator!=(value_type const& rhs) const 896 { 897 unique_lock<mutex_type> lk1(mtx_); 898 899 return value_ != rhs; 900 } operator <(value_type const & rhs) const901 bool operator<(value_type const& rhs) const 902 { 903 unique_lock<mutex_type> lk1(mtx_); 904 905 return value_ < rhs; 906 } operator <=(value_type const & rhs) const907 bool operator<=(value_type const& rhs) const 908 { 909 unique_lock<mutex_type> lk1(mtx_); 910 911 return value_ <= rhs; 912 } operator >(value_type const & rhs) const913 bool operator>(value_type const& rhs) const 914 { 915 unique_lock<mutex_type> lk1(mtx_); 916 917 return value_ > rhs; 918 } operator >=(value_type const & rhs) const919 bool operator>=(value_type const& rhs) const 920 { 921 unique_lock<mutex_type> lk1(mtx_); 922 923 return value_ >= rhs; 924 } 925 926 }; 927 928 // Specialized algorithms 929 /** 930 * 931 */ 932 template <typename T, typename L> swap(synchronized_value<T,L> & lhs,synchronized_value<T,L> & rhs)933 inline void swap(synchronized_value<T,L> & lhs, synchronized_value<T,L> & rhs) 934 { 935 lhs.swap(rhs); 936 } 937 template <typename T, typename L> swap(synchronized_value<T,L> & lhs,T & rhs)938 inline void swap(synchronized_value<T,L> & lhs, T & rhs) 939 { 940 lhs.swap(rhs); 941 } 942 template <typename T, typename L> swap(T & lhs,synchronized_value<T,L> & rhs)943 inline void swap(T & lhs, synchronized_value<T,L> & rhs) 944 { 945 rhs.swap(lhs); 946 } 947 948 //Hash support 949 950 // template <class T> struct hash; 951 // template <typename T, typename L> 952 // struct hash<synchronized_value<T,L> >; 953 954 // Comparison with T 955 template <typename T, typename L> operator !=(synchronized_value<T,L> const & lhs,synchronized_value<T,L> const & rhs)956 bool operator!=(synchronized_value<T,L> const&lhs, synchronized_value<T,L> const& rhs) 957 { 958 return ! (lhs==rhs); 959 } 960 961 template <typename T, typename L> operator ==(T const & lhs,synchronized_value<T,L> const & rhs)962 bool operator==(T const& lhs, synchronized_value<T,L> const&rhs) 963 { 964 return rhs==lhs; 965 } 966 template <typename T, typename L> operator !=(T const & lhs,synchronized_value<T,L> const & rhs)967 bool operator!=(T const& lhs, synchronized_value<T,L> const&rhs) 968 { 969 return rhs!=lhs; 970 } 971 template <typename T, typename L> operator <(T const & lhs,synchronized_value<T,L> const & rhs)972 bool operator<(T const& lhs, synchronized_value<T,L> const&rhs) 973 { 974 return rhs>lhs; 975 } 976 template <typename T, typename L> operator <=(T const & lhs,synchronized_value<T,L> const & rhs)977 bool operator<=(T const& lhs, synchronized_value<T,L> const&rhs) 978 { 979 return rhs>=lhs; 980 } 981 template <typename T, typename L> operator >(T const & lhs,synchronized_value<T,L> const & rhs)982 bool operator>(T const& lhs, synchronized_value<T,L> const&rhs) 983 { 984 return rhs<lhs; 985 } 986 template <typename T, typename L> operator >=(T const & lhs,synchronized_value<T,L> const & rhs)987 bool operator>=(T const& lhs, synchronized_value<T,L> const&rhs) 988 { 989 return rhs<=lhs; 990 } 991 992 /** 993 * 994 */ 995 template <typename OStream, typename T, typename L> operator <<(OStream & os,synchronized_value<T,L> const & rhs)996 inline OStream& operator<<(OStream& os, synchronized_value<T,L> const& rhs) 997 { 998 rhs.save(os); 999 return os; 1000 } 1001 template <typename IStream, typename T, typename L> operator >>(IStream & is,synchronized_value<T,L> & rhs)1002 inline IStream& operator>>(IStream& is, synchronized_value<T,L>& rhs) 1003 { 1004 rhs.load(is); 1005 return is; 1006 } 1007 1008 #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) 1009 #if ! defined BOOST_NO_CXX11_VARIADIC_TEMPLATES 1010 1011 template <typename ...SV> synchronize(SV &...sv)1012 std::tuple<typename synchronized_value_strict_lock_ptr<SV>::type ...> synchronize(SV& ...sv) 1013 { 1014 boost::lock(sv.mtx_ ...); 1015 typedef std::tuple<typename synchronized_value_strict_lock_ptr<SV>::type ...> t_type; 1016 1017 return t_type(typename synchronized_value_strict_lock_ptr<SV>::type(sv.value_, sv.mtx_, adopt_lock) ...); 1018 } 1019 #else 1020 1021 template <typename SV1, typename SV2> 1022 std::tuple< 1023 typename synchronized_value_strict_lock_ptr<SV1>::type, 1024 typename synchronized_value_strict_lock_ptr<SV2>::type 1025 > synchronize(SV1 & sv1,SV2 & sv2)1026 synchronize(SV1& sv1, SV2& sv2) 1027 { 1028 boost::lock(sv1.mtx_, sv2.mtx_); 1029 typedef std::tuple< 1030 typename synchronized_value_strict_lock_ptr<SV1>::type, 1031 typename synchronized_value_strict_lock_ptr<SV2>::type 1032 > t_type; 1033 1034 return t_type( 1035 typename synchronized_value_strict_lock_ptr<SV1>::type(sv1.value_, sv1.mtx_, adopt_lock), 1036 typename synchronized_value_strict_lock_ptr<SV2>::type(sv2.value_, sv2.mtx_, adopt_lock) 1037 ); 1038 1039 } 1040 template <typename SV1, typename SV2, typename SV3> 1041 std::tuple< 1042 typename synchronized_value_strict_lock_ptr<SV1>::type, 1043 typename synchronized_value_strict_lock_ptr<SV2>::type, 1044 typename synchronized_value_strict_lock_ptr<SV3>::type 1045 > synchronize(SV1 & sv1,SV2 & sv2,SV3 & sv3)1046 synchronize(SV1& sv1, SV2& sv2, SV3& sv3) 1047 { 1048 boost::lock(sv1.mtx_, sv2.mtx_); 1049 typedef std::tuple< 1050 typename synchronized_value_strict_lock_ptr<SV1>::type, 1051 typename synchronized_value_strict_lock_ptr<SV2>::type, 1052 typename synchronized_value_strict_lock_ptr<SV3>::type 1053 > t_type; 1054 1055 return t_type( 1056 typename synchronized_value_strict_lock_ptr<SV1>::type(sv1.value_, sv1.mtx_, adopt_lock), 1057 typename synchronized_value_strict_lock_ptr<SV2>::type(sv2.value_, sv2.mtx_, adopt_lock), 1058 typename synchronized_value_strict_lock_ptr<SV3>::type(sv3.value_, sv3.mtx_, adopt_lock) 1059 ); 1060 1061 } 1062 #endif 1063 #endif 1064 } 1065 1066 #include <boost/config/abi_suffix.hpp> 1067 1068 #endif // header 1069