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