xref: /llvm-project/offload/include/ExclusiveAccess.h (revision 330d8983d25d08580fc1642fea48b2473f47a9da)
1 //===---- ExclusiveAccess.h - Helper for exclusive access data structures -===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #ifndef OMPTARGET_EXCLUSIVE_ACCESS
12 #define OMPTARGET_EXCLUSIVE_ACCESS
13 
14 #include <cassert>
15 #include <cstddef>
16 #include <cstdint>
17 #include <mutex>
18 
19 /// Forward declaration.
20 template <typename Ty> struct Accessor;
21 
22 /// A protected object is a simple wrapper to allocate an object of type \p Ty
23 /// together with a mutex that guards accesses to the object. The only way to
24 /// access the object is through the "exclusive accessor" which will lock the
25 /// mutex accordingly.
26 template <typename Ty> struct ProtectedObj {
27   using AccessorTy = Accessor<Ty>;
28 
29   /// Get an exclusive access Accessor object. \p DoNotGetAccess allows to
30   /// create an accessor that is not owning anything based on a boolean
31   /// condition.
32   AccessorTy getExclusiveAccessor(bool DoNotGetAccess = false);
33 
34 private:
35   Ty Obj;
36   std::mutex Mtx;
37   friend struct Accessor<Ty>;
38 };
39 
40 /// Helper to provide transparent exclusive access to protected objects.
41 template <typename Ty> struct Accessor {
42   /// Default constructor does not own anything and cannot access anything.
43   Accessor() : Ptr(nullptr) {}
44 
45   /// Constructor to get exclusive access by locking the mutex protecting the
46   /// underlying object.
47   Accessor(ProtectedObj<Ty> &PO) : Ptr(&PO) { lock(); }
48 
49   /// Constructor to get exclusive access by taking it from \p Other.
50   Accessor(Accessor<Ty> &&Other) : Ptr(Other.Ptr) { Other.Ptr = nullptr; }
51 
52   Accessor(Accessor &Other) = delete;
53 
54   /// If the object is still owned when the lifetime ends we give up access.
55   ~Accessor() { unlock(); }
56 
57   /// Give up access to the underlying object, virtually "destroying" the
58   /// accessor even if the object is still life.
59   void destroy() {
60     unlock();
61     Ptr = nullptr;
62   }
63 
64   /// Provide transparent access to the underlying object.
65   Ty &operator*() {
66     assert(Ptr && "Trying to access an object through a non-owning (or "
67                   "destroyed) accessor!");
68     return Ptr->Obj;
69   }
70   Ty *operator->() {
71     assert(Ptr && "Trying to access an object through a non-owning (or "
72                   "destroyed) accessor!");
73     return &Ptr->Obj;
74   }
75 
76 private:
77   /// Lock the underlying object if there is one.
78   void lock() {
79     if (Ptr)
80       Ptr->Mtx.lock();
81   }
82 
83   /// Unlock the underlying object if there is one.
84   void unlock() {
85     if (Ptr)
86       Ptr->Mtx.unlock();
87   }
88 
89   /// Pointer to the underlying object or null if the accessor lost access,
90   /// e.g., after a destroy call.
91   ProtectedObj<Ty> *Ptr;
92 };
93 
94 template <typename Ty>
95 Accessor<Ty> ProtectedObj<Ty>::getExclusiveAccessor(bool DoNotGetAccess) {
96   if (DoNotGetAccess)
97     return Accessor<Ty>();
98   return Accessor<Ty>(*this);
99 }
100 
101 #endif
102