• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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