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