1 // 2 // Copyright 2021 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // SynchronizedValue.h: 7 // A class that ensures that the correct mutex is locked when the encapsulated data is accessed. 8 // Based on boost::synchronized_value, which probably becomes part of the next C++ standard. 9 // https://www.boost.org/doc/libs/1_76_0/doc/html/thread/sds.html#thread.sds.synchronized_valuesxxx 10 11 #ifndef COMMON_SYNCHRONIZEDVALUE_H_ 12 #define COMMON_SYNCHRONIZEDVALUE_H_ 13 14 #include "common/debug.h" 15 16 #include <mutex> 17 #include <type_traits> 18 19 namespace angle 20 { 21 22 template <typename T, typename Lockable = std::mutex> 23 class ConstStrictLockPtr 24 { 25 public: 26 using value_type = T; 27 using mutex_type = Lockable; 28 ConstStrictLockPtr(const T & value,Lockable & mutex)29 ConstStrictLockPtr(const T &value, Lockable &mutex) : mLock(mutex), mValue(value) {} ConstStrictLockPtr(const T & value,Lockable & mutex,std::adopt_lock_t)30 ConstStrictLockPtr(const T &value, Lockable &mutex, std::adopt_lock_t) noexcept 31 : mLock(mutex, std::adopt_lock), mValue(value) 32 {} 33 ConstStrictLockPtr(ConstStrictLockPtr && other)34 ConstStrictLockPtr(ConstStrictLockPtr &&other) noexcept 35 : mLock(std::move(other.mLock)), mValue(other.mValue) 36 {} 37 38 ConstStrictLockPtr(const ConstStrictLockPtr &) = delete; 39 ConstStrictLockPtr &operator=(const ConstStrictLockPtr &) = delete; 40 41 ~ConstStrictLockPtr() = default; 42 43 const T *operator->() const { return &mValue; } 44 const T &operator*() const { return mValue; } 45 46 protected: 47 std::unique_lock<Lockable> mLock; 48 T const &mValue; 49 }; 50 51 template <typename T, typename Lockable = std::mutex> 52 class StrictLockPtr : public ConstStrictLockPtr<T, Lockable> 53 { 54 private: 55 using BaseType = ConstStrictLockPtr<T, Lockable>; 56 57 public: StrictLockPtr(T & value,Lockable & mutex)58 StrictLockPtr(T &value, Lockable &mutex) : BaseType(value, mutex) {} StrictLockPtr(T & value,Lockable & mutex,std::adopt_lock_t)59 StrictLockPtr(T &value, Lockable &mutex, std::adopt_lock_t) noexcept 60 : BaseType(value, mutex, std::adopt_lock) 61 {} 62 StrictLockPtr(StrictLockPtr && other)63 StrictLockPtr(StrictLockPtr &&other) noexcept 64 : BaseType(std::move(static_cast<BaseType &&>(other))) 65 {} 66 67 StrictLockPtr(const StrictLockPtr &) = delete; 68 StrictLockPtr &operator=(const StrictLockPtr &) = delete; 69 70 ~StrictLockPtr() = default; 71 72 T *operator->() { return const_cast<T *>(&this->mValue); } 73 T &operator*() { return const_cast<T &>(this->mValue); } 74 }; 75 76 template <typename SV> 77 struct SynchronizedValueStrictLockPtr 78 { 79 using type = StrictLockPtr<typename SV::value_type, typename SV::mutex_type>; 80 }; 81 82 template <typename SV> 83 struct SynchronizedValueStrictLockPtr<const SV> 84 { 85 using type = ConstStrictLockPtr<typename SV::value_type, typename SV::mutex_type>; 86 }; 87 88 template <typename T, typename Lockable = std::mutex> 89 class ConstUniqueLockPtr : public std::unique_lock<Lockable> 90 { 91 private: 92 using BaseType = std::unique_lock<Lockable>; 93 94 public: 95 using value_type = T; 96 using mutex_type = Lockable; 97 98 ConstUniqueLockPtr(const T &value, Lockable &mutex) : BaseType(mutex), mValue(value) {} 99 ConstUniqueLockPtr(const T &value, Lockable &mutex, std::adopt_lock_t) noexcept 100 : BaseType(mutex, std::adopt_lock), mValue(value) 101 {} 102 ConstUniqueLockPtr(const T &value, Lockable &mutex, std::defer_lock_t) noexcept 103 : BaseType(mutex, std::defer_lock), mValue(value) 104 {} 105 ConstUniqueLockPtr(const T &value, Lockable &mutex, std::try_to_lock_t) noexcept 106 : BaseType(mutex, std::try_to_lock), mValue(value) 107 {} 108 109 ConstUniqueLockPtr(ConstUniqueLockPtr &&other) noexcept 110 : BaseType(std::move(static_cast<BaseType &&>(other))), mValue(other.mValue) 111 {} 112 113 ConstUniqueLockPtr(const ConstUniqueLockPtr &) = delete; 114 ConstUniqueLockPtr &operator=(const ConstUniqueLockPtr &) = delete; 115 116 ~ConstUniqueLockPtr() = default; 117 118 const T *operator->() const 119 { 120 ASSERT(this->owns_lock()); 121 return &mValue; 122 } 123 const T &operator*() const 124 { 125 ASSERT(this->owns_lock()); 126 return mValue; 127 } 128 129 protected: 130 T const &mValue; 131 }; 132 133 template <typename T, typename Lockable = std::mutex> 134 class UniqueLockPtr : public ConstUniqueLockPtr<T, Lockable> 135 { 136 private: 137 using BaseType = ConstUniqueLockPtr<T, Lockable>; 138 139 public: 140 UniqueLockPtr(T &value, Lockable &mutex) : BaseType(value, mutex) {} 141 UniqueLockPtr(T &value, Lockable &mutex, std::adopt_lock_t) noexcept 142 : BaseType(value, mutex, std::adopt_lock) 143 {} 144 UniqueLockPtr(T &value, Lockable &mutex, std::defer_lock_t) noexcept 145 : BaseType(value, mutex, std::defer_lock) 146 {} 147 UniqueLockPtr(T &value, Lockable &mutex, std::try_to_lock_t) noexcept 148 : BaseType(value, mutex, std::try_to_lock) 149 {} 150 151 UniqueLockPtr(UniqueLockPtr &&other) noexcept 152 : BaseType(std::move(static_cast<BaseType &&>(other))) 153 {} 154 155 UniqueLockPtr(const UniqueLockPtr &) = delete; 156 UniqueLockPtr &operator=(const UniqueLockPtr &) = delete; 157 158 ~UniqueLockPtr() = default; 159 160 T *operator->() 161 { 162 ASSERT(this->owns_lock()); 163 return const_cast<T *>(&this->mValue); 164 } 165 T &operator*() 166 { 167 ASSERT(this->owns_lock()); 168 return const_cast<T &>(this->mValue); 169 } 170 }; 171 172 template <typename SV> 173 struct SynchronizedValueUniqueLockPtr 174 { 175 using type = UniqueLockPtr<typename SV::value_type, typename SV::mutex_type>; 176 }; 177 178 template <typename SV> 179 struct SynchronizedValueUniqueLockPtr<const SV> 180 { 181 using type = ConstUniqueLockPtr<typename SV::value_type, typename SV::mutex_type>; 182 }; 183 184 template <typename T, typename Lockable = std::mutex> 185 class SynchronizedValue 186 { 187 public: 188 using value_type = T; 189 using mutex_type = Lockable; 190 191 SynchronizedValue() noexcept(std::is_nothrow_default_constructible<T>::value) : mValue() {} 192 193 SynchronizedValue(const T &other) noexcept(std::is_nothrow_copy_constructible<T>::value) 194 : mValue(other) 195 {} 196 197 SynchronizedValue(T &&other) noexcept(std::is_nothrow_move_constructible<T>::value) 198 : mValue(std::move(other)) 199 {} 200 201 template <typename... Args> 202 SynchronizedValue(Args &&... args) noexcept(noexcept(T(std::forward<Args>(args)...))) 203 : mValue(std::forward<Args>(args)...) 204 {} 205 206 SynchronizedValue(const SynchronizedValue &other) 207 { 208 std::lock_guard<Lockable> lock(other.mMutex); 209 mValue = other.mValue; 210 } 211 212 SynchronizedValue(SynchronizedValue &&other) 213 { 214 std::lock_guard<Lockable> lock(other.mMutex); 215 mValue = std::move(other.mValue); 216 } 217 218 SynchronizedValue &operator=(const SynchronizedValue &other) 219 { 220 if (&other != this) 221 { 222 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 223 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 224 std::lock(lock1, lock2); 225 mValue = other.mValue; 226 } 227 return *this; 228 } 229 230 SynchronizedValue &operator=(SynchronizedValue &&other) 231 { 232 if (&other != this) 233 { 234 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 235 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 236 std::lock(lock1, lock2); 237 mValue = std::move(other.mValue); 238 } 239 return *this; 240 } 241 242 SynchronizedValue &operator=(const T &value) 243 { 244 { 245 std::lock_guard<Lockable> lock(mMutex); 246 mValue = value; 247 } 248 return *this; 249 } 250 251 SynchronizedValue &operator=(T &&value) 252 { 253 { 254 std::lock_guard<Lockable> lock(mMutex); 255 mValue = std::move(value); 256 } 257 return *this; 258 } 259 260 T get() const 261 { 262 std::lock_guard<Lockable> lock(mMutex); 263 return mValue; 264 } 265 266 explicit operator T() const { return get(); } 267 268 void swap(SynchronizedValue &other) 269 { 270 if (this == &other) 271 { 272 return; 273 } 274 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 275 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 276 std::lock(lock1, lock2); 277 std::swap(mValue, other.mValue); 278 } 279 280 void swap(T &other) 281 { 282 std::lock_guard<Lockable> lock(mMutex); 283 std::swap(mValue, other); 284 } 285 286 StrictLockPtr<T, Lockable> operator->() { return StrictLockPtr<T, Lockable>(mValue, mMutex); } 287 ConstStrictLockPtr<T, Lockable> operator->() const 288 { 289 return ConstStrictLockPtr<T, Lockable>(mValue, mMutex); 290 } 291 292 StrictLockPtr<T, Lockable> synchronize() { return StrictLockPtr<T, Lockable>(mValue, mMutex); } 293 ConstStrictLockPtr<T, Lockable> synchronize() const 294 { 295 return ConstStrictLockPtr<T, Lockable>(mValue, mMutex); 296 } 297 298 UniqueLockPtr<T, Lockable> unique_synchronize() 299 { 300 return UniqueLockPtr<T, Lockable>(mValue, mMutex); 301 } 302 ConstUniqueLockPtr<T, Lockable> unique_synchronize() const 303 { 304 return ConstUniqueLockPtr<T, Lockable>(mValue, mMutex); 305 } 306 307 UniqueLockPtr<T, Lockable> defer_synchronize() noexcept 308 { 309 return UniqueLockPtr<T, Lockable>(mValue, mMutex, std::defer_lock); 310 } 311 ConstUniqueLockPtr<T, Lockable> defer_synchronize() const noexcept 312 { 313 return ConstUniqueLockPtr<T, Lockable>(mValue, mMutex, std::defer_lock); 314 } 315 316 UniqueLockPtr<T, Lockable> try_to_synchronize() noexcept 317 { 318 return UniqueLockPtr<T, Lockable>(mValue, mMutex, std::try_to_lock); 319 } 320 ConstUniqueLockPtr<T, Lockable> try_to_synchronize() const noexcept 321 { 322 return ConstUniqueLockPtr<T, Lockable>(mValue, mMutex, std::try_to_lock); 323 } 324 325 UniqueLockPtr<T, Lockable> adopt_synchronize() noexcept 326 { 327 return UniqueLockPtr<T, Lockable>(mValue, mMutex, std::adopt_lock); 328 } 329 ConstUniqueLockPtr<T, Lockable> adopt_synchronize() const noexcept 330 { 331 return ConstUniqueLockPtr<T, Lockable>(mValue, mMutex, std::adopt_lock); 332 } 333 334 class DerefValue 335 { 336 public: 337 DerefValue(DerefValue &&other) : mLock(std::move(other.mLock)), mValue(other.mValue) {} 338 339 DerefValue(const DerefValue &) = delete; 340 DerefValue &operator=(const DerefValue &) = delete; 341 342 operator T &() { return mValue; } 343 344 DerefValue &operator=(const T &other) 345 { 346 mValue = other; 347 return *this; 348 } 349 350 private: 351 explicit DerefValue(SynchronizedValue &outer) : mLock(outer.mMutex), mValue(outer.mValue) {} 352 353 std::unique_lock<Lockable> mLock; 354 T &mValue; 355 356 friend class SynchronizedValue; 357 }; 358 359 class ConstDerefValue 360 { 361 public: 362 ConstDerefValue(ConstDerefValue &&other) 363 : mLock(std::move(other.mLock)), mValue(other.mValue) 364 {} 365 366 ConstDerefValue(const ConstDerefValue &) = delete; 367 ConstDerefValue &operator=(const ConstDerefValue &) = delete; 368 369 operator const T &() { return mValue; } 370 371 private: 372 explicit ConstDerefValue(const SynchronizedValue &outer) 373 : mLock(outer.mMutex), mValue(outer.mValue) 374 {} 375 376 std::unique_lock<Lockable> mLock; 377 const T &mValue; 378 379 friend class SynchronizedValue; 380 }; 381 382 DerefValue operator*() { return DerefValue(*this); } 383 ConstDerefValue operator*() const { return ConstDerefValue(*this); } 384 385 template <typename OStream> 386 void save(OStream &os) const 387 { 388 std::lock_guard<Lockable> lock(mMutex); 389 os << mValue; 390 } 391 392 template <typename IStream> 393 void load(IStream &is) 394 { 395 std::lock_guard<Lockable> lock(mMutex); 396 is >> mValue; 397 } 398 399 bool operator==(const SynchronizedValue &other) const 400 { 401 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 402 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 403 std::lock(lock1, lock2); 404 return mValue == other.mValue; 405 } 406 407 bool operator!=(const SynchronizedValue &other) const 408 { 409 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 410 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 411 std::lock(lock1, lock2); 412 return mValue != other.mValue; 413 } 414 415 bool operator<(const SynchronizedValue &other) const 416 { 417 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 418 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 419 std::lock(lock1, lock2); 420 return mValue < other.mValue; 421 } 422 423 bool operator>(const SynchronizedValue &other) const 424 { 425 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 426 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 427 std::lock(lock1, lock2); 428 return mValue > other.mValue; 429 } 430 431 bool operator<=(const SynchronizedValue &other) const 432 { 433 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 434 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 435 std::lock(lock1, lock2); 436 return mValue <= other.mValue; 437 } 438 439 bool operator>=(const SynchronizedValue &other) const 440 { 441 std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); 442 std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); 443 std::lock(lock1, lock2); 444 return mValue >= other.mValue; 445 } 446 447 bool operator==(const T &other) const 448 { 449 std::lock_guard<Lockable> lock(mMutex); 450 return mValue == other; 451 } 452 453 bool operator!=(const T &other) const 454 { 455 std::lock_guard<Lockable> lock(mMutex); 456 return mValue != other; 457 } 458 459 bool operator<(const T &other) const 460 { 461 std::lock_guard<Lockable> lock(mMutex); 462 return mValue < other; 463 } 464 465 bool operator>(const T &other) const 466 { 467 std::lock_guard<Lockable> lock(mMutex); 468 return mValue > other; 469 } 470 471 bool operator<=(const T &other) const 472 { 473 std::lock_guard<Lockable> lock(mMutex); 474 return mValue <= other; 475 } 476 477 bool operator>=(const T &other) const 478 { 479 std::lock_guard<Lockable> lock(mMutex); 480 return mValue >= other; 481 } 482 483 private: 484 T mValue; 485 mutable Lockable mMutex; 486 }; 487 488 template <typename OStream, typename T, typename L> 489 inline OStream &operator<<(OStream &os, SynchronizedValue<T, L> const &sv) 490 { 491 sv.save(os); 492 return os; 493 } 494 495 template <typename IStream, typename T, typename L> 496 inline IStream &operator>>(IStream &is, SynchronizedValue<T, L> &sv) 497 { 498 sv.load(is); 499 return is; 500 } 501 502 template <typename T, typename L> 503 bool operator==(const T &lhs, const SynchronizedValue<T, L> &rhs) 504 { 505 return rhs == lhs; 506 } 507 508 template <typename T, typename L> 509 bool operator!=(const T &lhs, const SynchronizedValue<T, L> &rhs) 510 { 511 return rhs != lhs; 512 } 513 514 template <typename T, typename L> 515 bool operator<(const T &lhs, const SynchronizedValue<T, L> &rhs) 516 { 517 return rhs < lhs; 518 } 519 520 template <typename T, typename L> 521 bool operator>(const T &lhs, const SynchronizedValue<T, L> &rhs) 522 { 523 return rhs > lhs; 524 } 525 526 template <typename T, typename L> 527 bool operator<=(const T &lhs, const SynchronizedValue<T, L> &rhs) 528 { 529 return rhs <= lhs; 530 } 531 532 template <typename T, typename L> 533 bool operator>=(const T &lhs, const SynchronizedValue<T, L> &rhs) 534 { 535 return rhs >= lhs; 536 } 537 538 } // namespace angle 539 540 #endif // COMMON_SYNCHRONIZEDVALUE_H_ 541