forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[OpenMP][FIX] Ensure exclusive access to the HDTT map
This patch solves two problems with the `HostDataToTargetMap` (HDTT map) which caused races and crashes before: 1) Any access to the HDTT map needs to be exclusive access. This was not the case for the "dump table" traversals that could collide with updates by other threads. The new `Accessor` and `ProtectedObject` wrappers will ensure we have a hard time introducing similar races in the future. Note that we could allow multiple concurrent read-accesses but that feature can be added to the `Accessor` API later. 2) The elements of the HDTT map were `HostDataToTargetTy` objects which meant that they could be copied/moved/deleted as the map was changed. However, we sometimes kept pointers to these elements around after we gave up the map lock which caused potential races again. The new indirection through `HostDataToTargetMapKeyTy` will allows us to modify the map while keeping the (interesting part of the) entries valid. To offset potential cost we duplicate the ordering key of the entry which avoids an additional indirect lookup. We should replace more objects with "protected objects" as we go. Differential Revision: https://reviews.llvm.org/D121057
- Loading branch information
Showing
5 changed files
with
234 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
//===---- 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 <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 std::move(Accessor<Ty>()); | ||
return std::move(Accessor<Ty>(*this)); | ||
} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.