| //===---- ExclusiveAccess.h - Helper for exclusive access data structures -===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef OMPTARGET_EXCLUSIVE_ACCESS |
| #define OMPTARGET_EXCLUSIVE_ACCESS |
| |
| #include <cassert> |
| #include <cstddef> |
| #include <cstdint> |
| #include <mutex> |
| |
| /// Forward declaration. |
| template <typename Ty> struct Accessor; |
| |
| /// A protected object is a simple wrapper to allocate an object of type \p Ty |
| /// together with a mutex that guards accesses to the object. The only way to |
| /// access the object is through the "exclusive accessor" which will lock the |
| /// mutex accordingly. |
| template <typename Ty> struct ProtectedObj { |
| using AccessorTy = Accessor<Ty>; |
| |
| /// Get an exclusive access Accessor object. \p DoNotGetAccess allows to |
| /// create an accessor that is not owning anything based on a boolean |
| /// condition. |
| AccessorTy getExclusiveAccessor(bool DoNotGetAccess = false); |
| |
| private: |
| Ty Obj; |
| std::mutex Mtx; |
| friend struct Accessor<Ty>; |
| }; |
| |
| /// Helper to provide transparent exclusive access to protected objects. |
| template <typename Ty> struct Accessor { |
| /// Default constructor does not own anything and cannot access anything. |
| Accessor() : Ptr(nullptr) {} |
| |
| /// Constructor to get exclusive access by locking the mutex protecting the |
| /// underlying object. |
| Accessor(ProtectedObj<Ty> &PO) : Ptr(&PO) { lock(); } |
| |
| /// Constructor to get exclusive access by taking it from \p Other. |
| Accessor(Accessor<Ty> &&Other) : Ptr(Other.Ptr) { Other.Ptr = nullptr; } |
| |
| Accessor(Accessor &Other) = delete; |
| |
| /// If the object is still owned when the lifetime ends we give up access. |
| ~Accessor() { unlock(); } |
| |
| /// Give up access to the underlying object, virtually "destroying" the |
| /// accessor even if the object is still life. |
| void destroy() { |
| unlock(); |
| Ptr = nullptr; |
| } |
| |
| /// Provide transparent access to the underlying object. |
| Ty &operator*() { |
| assert(Ptr && "Trying to access an object through a non-owning (or " |
| "destroyed) accessor!"); |
| return Ptr->Obj; |
| } |
| Ty *operator->() { |
| assert(Ptr && "Trying to access an object through a non-owning (or " |
| "destroyed) accessor!"); |
| return &Ptr->Obj; |
| } |
| |
| private: |
| /// Lock the underlying object if there is one. |
| void lock() { |
| if (Ptr) |
| Ptr->Mtx.lock(); |
| } |
| |
| /// Unlock the underlying object if there is one. |
| void unlock() { |
| if (Ptr) |
| Ptr->Mtx.unlock(); |
| } |
| |
| /// Pointer to the underlying object or null if the accessor lost access, |
| /// e.g., after a destroy call. |
| ProtectedObj<Ty> *Ptr; |
| }; |
| |
| template <typename Ty> |
| Accessor<Ty> ProtectedObj<Ty>::getExclusiveAccessor(bool DoNotGetAccess) { |
| if (DoNotGetAccess) |
| return Accessor<Ty>(); |
| return Accessor<Ty>(*this); |
| } |
| |
| #endif |