Rework memory locks to allow device parallelism in alloc/free.
Prior solution used a single global lock to protect the memory tracking structures.
This change protects the memory tracking structure with a shared mutex (rw lock) in
shared (r) mode for memory allocations and frees so that long duration processes,
calling to kfd, can be done in parallel. Operations which must modify the memory map
take the mutex in exclusive mode (w) and must not call to the thunk while holding
the mutex.
The fragment allocator now requires separate protection and is protected with a
mutex at the device level. Protecting at the device level, rather than pool,
allows retention of the current recursive design and allows calling Trim from
withing Allocate. This could be made finer (pool level locks) but would
require backing out of Allocate entirely to call Trim. Trim and any retried
Allocation must be done in isolation (per device) or we may report OOM when
memory is actually available in some pool's fragment cache. So some device
level serialization is required in at least some paths.
Change-Id: I7c1e94d6965ffcc602b12fefdd3a6e97b84b5e00
[ROCm/ROCR-Runtime commit: df55cb0450]
Cette révision appartient à :
@@ -53,8 +53,15 @@
|
||||
#include "core/inc/queue.h"
|
||||
#include "core/inc/memory_region.h"
|
||||
#include "core/util/utils.h"
|
||||
#include "core/util/locks.h"
|
||||
|
||||
namespace rocr {
|
||||
|
||||
// Forward declare AMD::MemoryRegion
|
||||
namespace AMD {
|
||||
class MemoryRegion;
|
||||
}
|
||||
|
||||
namespace core {
|
||||
class Signal;
|
||||
|
||||
@@ -65,6 +72,8 @@ typedef void (*HsaEventCallback)(hsa_status_t status, hsa_queue_t* source,
|
||||
// replaced by tools libraries. All funtions other than Convert, node_id,
|
||||
// device_type, and public_handle must be virtual.
|
||||
class Agent : public Checked<0xF6BC25EB17E6F917> {
|
||||
friend class rocr::AMD::MemoryRegion;
|
||||
|
||||
public:
|
||||
// @brief Convert agent object into hsa_agent_t.
|
||||
//
|
||||
@@ -297,6 +306,12 @@ class Agent : public Checked<0xF6BC25EB17E6F917> {
|
||||
|
||||
bool profiling_enabled_;
|
||||
|
||||
// Used by an Agent's MemoryRegions to ensure serial memory operation on the device.
|
||||
// Serial memory operations are needed to ensure, among other things, that allocation failures are
|
||||
// due to true OOM conditions and per region caching (Trim and Allocate must be serial and
|
||||
// exclusive to ensure this).
|
||||
KernelMutex agent_memory_lock_;
|
||||
|
||||
// Forbid copying and moving of this object
|
||||
DISALLOW_COPY_AND_ASSIGN(Agent);
|
||||
};
|
||||
|
||||
@@ -185,6 +185,8 @@ class MemoryRegion : public core::MemoryRegion {
|
||||
|
||||
HSAuint64 virtual_size_;
|
||||
|
||||
// Protects against concurrent allow_access calls to fragments of the same block by virtue of all
|
||||
// fragments of the block routing to the same MemoryRegion.
|
||||
mutable KernelMutex access_lock_;
|
||||
|
||||
static const size_t kPageSize_ = 4096;
|
||||
@@ -193,6 +195,12 @@ class MemoryRegion : public core::MemoryRegion {
|
||||
hsa_amd_memory_pool_access_t GetAccessInfo(const core::Agent& agent,
|
||||
const core::Runtime::LinkInfo& link_info) const;
|
||||
|
||||
// Operational body for Allocate. Recursive.
|
||||
hsa_status_t AllocateImpl(size_t& size, AllocateFlags alloc_flags, void** address) const;
|
||||
|
||||
// Operational body for Free. Recursive.
|
||||
hsa_status_t FreeImpl(void* address, size_t size) const;
|
||||
|
||||
class BlockAllocator {
|
||||
private:
|
||||
MemoryRegion& region_;
|
||||
@@ -200,7 +208,7 @@ class MemoryRegion : public core::MemoryRegion {
|
||||
public:
|
||||
explicit BlockAllocator(MemoryRegion& region) : region_(region) {}
|
||||
void* alloc(size_t request_size, size_t& allocated_size) const;
|
||||
void free(void* ptr, size_t length) const { region_.Free(ptr, length); }
|
||||
void free(void* ptr, size_t length) const { region_.FreeImpl(ptr, length); }
|
||||
size_t block_size() const { return block_size_; }
|
||||
};
|
||||
|
||||
|
||||
@@ -490,10 +490,11 @@ class Runtime {
|
||||
/// @brief Get the highest used node id.
|
||||
uint32_t max_node_id() const { return agents_by_node_.rbegin()->first; }
|
||||
|
||||
// Mutex object to protect multithreaded access to ::allocation_map_,
|
||||
// Mutex object to protect multithreaded access to ::allocation_map_.
|
||||
// Also ensures atomicity of pointer info queries by interlocking
|
||||
// KFD map/unmap, register/unregister, and access to hsaKmtQueryPointerInfo
|
||||
// registered & mapped arrays.
|
||||
KernelMutex memory_lock_;
|
||||
KernelSharedMutex memory_lock_;
|
||||
|
||||
// Array containing tools library handles.
|
||||
std::vector<os::LibHandle> tool_libs_;
|
||||
|
||||
@@ -157,6 +157,12 @@ MemoryRegion::MemoryRegion(bool fine_grain, bool kernarg, bool full_profile, cor
|
||||
MemoryRegion::~MemoryRegion() {}
|
||||
|
||||
hsa_status_t MemoryRegion::Allocate(size_t& size, AllocateFlags alloc_flags, void** address) const {
|
||||
ScopedAcquire<KernelMutex> lock(&owner()->agent_memory_lock_);
|
||||
return AllocateImpl(size, alloc_flags, address);
|
||||
}
|
||||
|
||||
hsa_status_t MemoryRegion::AllocateImpl(size_t& size, AllocateFlags alloc_flags,
|
||||
void** address) const {
|
||||
if (address == NULL) {
|
||||
return HSA_STATUS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
@@ -251,6 +257,11 @@ hsa_status_t MemoryRegion::Allocate(size_t& size, AllocateFlags alloc_flags, voi
|
||||
}
|
||||
|
||||
hsa_status_t MemoryRegion::Free(void* address, size_t size) const {
|
||||
ScopedAcquire<KernelMutex> lock(&owner()->agent_memory_lock_);
|
||||
return FreeImpl(address, size);
|
||||
}
|
||||
|
||||
hsa_status_t MemoryRegion::FreeImpl(void* address, size_t size) const {
|
||||
if (fragment_allocator_.free(address)) return HSA_STATUS_SUCCESS;
|
||||
|
||||
MakeKfdMemoryUnresident(address);
|
||||
@@ -262,6 +273,7 @@ hsa_status_t MemoryRegion::Free(void* address, size_t size) const {
|
||||
|
||||
// TODO: Look into a better name and/or making this process transparent to exporting.
|
||||
hsa_status_t MemoryRegion::IPCFragmentExport(void* address) const {
|
||||
ScopedAcquire<KernelMutex> lock(&owner()->agent_memory_lock_);
|
||||
if (!fragment_allocator_.discardBlock(address)) return HSA_STATUS_ERROR_INVALID_ALLOCATION;
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -583,8 +595,10 @@ hsa_status_t MemoryRegion::AllowAccess(uint32_t num_agents,
|
||||
HsaMemMapFlags map_flag = map_flag_;
|
||||
map_flag.ui32.HostAccess |= (cpu_in_list) ? 1 : 0;
|
||||
|
||||
{
|
||||
ScopedAcquire<KernelMutex> lock(&core::Runtime::runtime_singleton_->memory_lock_);
|
||||
{ // Sequence with pointer info since queries to other fragments of the block may be adjusted by
|
||||
// this call.
|
||||
ScopedAcquire<KernelSharedMutex::Shared> lock(
|
||||
core::Runtime::runtime_singleton_->memory_lock_.shared());
|
||||
uint64_t alternate_va = 0;
|
||||
if (!AMD::MemoryRegion::MakeKfdMemoryResident(
|
||||
whitelist_nodes.size(), &whitelist_nodes[0], ptr,
|
||||
@@ -593,8 +607,6 @@ hsa_status_t MemoryRegion::AllowAccess(uint32_t num_agents,
|
||||
}
|
||||
}
|
||||
|
||||
lock.Release();
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -700,7 +712,7 @@ void* MemoryRegion::BlockAllocator::alloc(size_t request_size, size_t& allocated
|
||||
void* ret;
|
||||
size_t bsize = AlignUp(request_size, block_size());
|
||||
|
||||
hsa_status_t err = region_.Allocate(
|
||||
hsa_status_t err = region_.AllocateImpl(
|
||||
bsize, core::MemoryRegion::AllocateRestrict | core::MemoryRegion::AllocateDirect, &ret);
|
||||
if (err != HSA_STATUS_SUCCESS)
|
||||
throw AMD::hsa_exception(err, "MemoryRegion::BlockAllocator::alloc failed.");
|
||||
|
||||
@@ -276,11 +276,10 @@ hsa_status_t Runtime::IterateAgent(hsa_status_t (*callback)(hsa_agent_t agent,
|
||||
hsa_status_t Runtime::AllocateMemory(const MemoryRegion* region, size_t size,
|
||||
MemoryRegion::AllocateFlags alloc_flags,
|
||||
void** address) {
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
hsa_status_t status = region->Allocate(size, alloc_flags, address);
|
||||
|
||||
// Track the allocation result so that it could be freed properly.
|
||||
if (status == HSA_STATUS_SUCCESS) {
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
allocation_map_[*address] = AllocationRegion(region, size);
|
||||
}
|
||||
|
||||
@@ -297,7 +296,7 @@ hsa_status_t Runtime::FreeMemory(void* ptr) {
|
||||
std::unique_ptr<std::vector<AllocationRegion::notifier_t>> notifiers;
|
||||
|
||||
{
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
|
||||
std::map<const void*, AllocationRegion>::iterator it = allocation_map_.find(ptr);
|
||||
|
||||
@@ -317,26 +316,23 @@ hsa_status_t Runtime::FreeMemory(void* ptr) {
|
||||
notifiers = std::move(it->second.notifiers);
|
||||
|
||||
allocation_map_.erase(it);
|
||||
|
||||
// Fast path to avoid doubling lock ops in the common case (no notifiers).
|
||||
if (!notifiers) return region->Free(ptr, size);
|
||||
}
|
||||
|
||||
// Notifiers can't run while holding the lock or the callback won't be able to manage memory.
|
||||
// The memory triggering the notification has already been removed from the memory map so can't
|
||||
// be double released during the callback.
|
||||
for (auto& notifier : *notifiers) {
|
||||
notifier.callback(notifier.ptr, notifier.user_data);
|
||||
if (notifiers) {
|
||||
for (auto& notifier : *notifiers) {
|
||||
notifier.callback(notifier.ptr, notifier.user_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Fragment allocator requires protection.
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
return region->Free(ptr, size);
|
||||
}
|
||||
|
||||
hsa_status_t Runtime::RegisterReleaseNotifier(void* ptr, hsa_amd_deallocation_callback_t callback,
|
||||
void* user_data) {
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
auto mem = allocation_map_.upper_bound(ptr);
|
||||
if (mem != allocation_map_.begin()) {
|
||||
mem--;
|
||||
@@ -360,7 +356,7 @@ hsa_status_t Runtime::RegisterReleaseNotifier(void* ptr, hsa_amd_deallocation_ca
|
||||
hsa_status_t Runtime::DeregisterReleaseNotifier(void* ptr,
|
||||
hsa_amd_deallocation_callback_t callback) {
|
||||
hsa_status_t ret = HSA_STATUS_ERROR_INVALID_ARGUMENT;
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
auto mem = allocation_map_.upper_bound(ptr);
|
||||
if (mem != allocation_map_.begin()) {
|
||||
mem--;
|
||||
@@ -560,7 +556,7 @@ hsa_status_t Runtime::AllowAccess(uint32_t num_agents,
|
||||
size_t alloc_size = 0;
|
||||
|
||||
{
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
|
||||
std::map<const void*, AllocationRegion>::const_iterator it = allocation_map_.find(ptr);
|
||||
|
||||
@@ -786,7 +782,7 @@ hsa_status_t Runtime::PtrInfo(const void* ptr, hsa_amd_pointer_info_t* info, voi
|
||||
|
||||
{ // memory_lock protects access to the NMappedNodes array and fragment user data since these may
|
||||
// change with calls to memory APIs.
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
|
||||
// We don't care if this returns an error code.
|
||||
// The type will be HSA_EXT_POINTER_TYPE_UNKNOWN if so.
|
||||
@@ -886,7 +882,7 @@ hsa_status_t Runtime::PtrInfo(const void* ptr, hsa_amd_pointer_info_t* info, voi
|
||||
|
||||
hsa_status_t Runtime::SetPtrInfoData(const void* ptr, void* userptr) {
|
||||
{ // Use allocation map if possible to handle fragments.
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
const auto& it = allocation_map_.find(ptr);
|
||||
if (it != allocation_map_.end()) {
|
||||
it->second.user_ptr = userptr;
|
||||
@@ -926,7 +922,7 @@ hsa_status_t Runtime::IPCCreate(void* ptr, size_t len, hsa_amd_ipc_memory_t* han
|
||||
// Holds size in (4K?) pages in thunk handle: Mark as a fragment and denote offset.
|
||||
handle->handle[6] |= 0x80000000 | offset;
|
||||
// Mark block for IPC. Prevents reallocation of exported memory.
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex::Shared> lock(memory_lock_.shared());
|
||||
hsa_status_t err = allocation_map_[ptr].region->IPCFragmentExport(ptr);
|
||||
assert(err == HSA_STATUS_SUCCESS && "Region inconsistent with address map.");
|
||||
return err;
|
||||
@@ -951,11 +947,12 @@ hsa_status_t Runtime::IPCAttach(const hsa_amd_ipc_memory_t* handle, size_t len,
|
||||
// Extract fragment info
|
||||
bool isFragment = false;
|
||||
uint32_t fragOffset = 0;
|
||||
|
||||
auto fixFragment = [&]() {
|
||||
if (!isFragment) return;
|
||||
importAddress = reinterpret_cast<uint8_t*>(importAddress) + fragOffset;
|
||||
len = Min(len, importSize - fragOffset);
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
allocation_map_[importAddress] = AllocationRegion(nullptr, len);
|
||||
};
|
||||
|
||||
@@ -1017,7 +1014,7 @@ hsa_status_t Runtime::IPCAttach(const hsa_amd_ipc_memory_t* handle, size_t len,
|
||||
|
||||
hsa_status_t Runtime::IPCDetach(void* ptr) {
|
||||
{ // Handle imported fragments.
|
||||
ScopedAcquire<KernelMutex> lock(&memory_lock_);
|
||||
ScopedAcquire<KernelSharedMutex> lock(&memory_lock_);
|
||||
const auto& it = allocation_map_.find(ptr);
|
||||
if (it != allocation_map_.end()) {
|
||||
if (it->second.region != nullptr) return HSA_STATUS_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
@@ -168,6 +168,7 @@ class os_thread {
|
||||
|
||||
static_assert(sizeof(LibHandle) == sizeof(void*), "OS abstraction size mismatch");
|
||||
static_assert(sizeof(Mutex) == sizeof(pthread_mutex_t*), "OS abstraction size mismatch");
|
||||
static_assert(sizeof(SharedMutex) == sizeof(pthread_rwlock_t*), "OS abstraction size mismatch");
|
||||
static_assert(sizeof(Thread) == sizeof(os_thread*), "OS abstraction size mismatch");
|
||||
|
||||
LibHandle LoadLib(std::string filename) {
|
||||
@@ -457,6 +458,63 @@ uint64_t AccurateClockFrequency() {
|
||||
if (invPeriod == 0.0) invPeriod = 1.0 / double(time.tv_nsec);
|
||||
return 1000000000ull / uint64_t(time.tv_nsec);
|
||||
}
|
||||
|
||||
SharedMutex CreateSharedMutex() {
|
||||
pthread_rwlockattr_t attrib;
|
||||
int err = pthread_rwlockattr_init(&attrib);
|
||||
if (err != 0) {
|
||||
assert(false && "rw lock attribute init failed.");
|
||||
return nullptr;
|
||||
}
|
||||
err = pthread_rwlockattr_setkind_np(&attrib, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||
assert(err == 0 && "Set rw lock attribute failure.");
|
||||
|
||||
pthread_rwlock_t* lock = new pthread_rwlock_t;
|
||||
err = pthread_rwlock_init(lock, &attrib);
|
||||
if (err != 0) {
|
||||
assert(false && "rw lock init failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pthread_rwlockattr_destroy(&attrib);
|
||||
return lock;
|
||||
}
|
||||
|
||||
bool TryAcquireSharedMutex(SharedMutex lock) {
|
||||
int err = pthread_rwlock_trywrlock(*(pthread_rwlock_t**)&lock);
|
||||
return err == 0;
|
||||
}
|
||||
|
||||
bool AcquireSharedMutex(SharedMutex lock) {
|
||||
int err = pthread_rwlock_wrlock(*(pthread_rwlock_t**)&lock);
|
||||
return err == 0;
|
||||
}
|
||||
|
||||
void ReleaseSharedMutex(SharedMutex lock) {
|
||||
int err = pthread_rwlock_unlock(*(pthread_rwlock_t**)&lock);
|
||||
assert(err == 0 && "SharedMutex unlock failed.");
|
||||
}
|
||||
|
||||
bool TrySharedAcquireSharedMutex(SharedMutex lock) {
|
||||
int err = pthread_rwlock_tryrdlock(*(pthread_rwlock_t**)&lock);
|
||||
return err == 0;
|
||||
}
|
||||
|
||||
bool SharedAcquireSharedMutex(SharedMutex lock) {
|
||||
int err = pthread_rwlock_rdlock(*(pthread_rwlock_t**)&lock);
|
||||
return err == 0;
|
||||
}
|
||||
|
||||
void SharedReleaseSharedMutex(SharedMutex lock) {
|
||||
int err = pthread_rwlock_unlock(*(pthread_rwlock_t**)&lock);
|
||||
assert(err == 0 && "SharedMutex unlock failed.");
|
||||
}
|
||||
|
||||
void DestroySharedMutex(SharedMutex lock) {
|
||||
pthread_rwlock_destroy(*(pthread_rwlock_t**)&lock);
|
||||
delete *(pthread_rwlock_t**)&lock;
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
} // namespace rocr
|
||||
|
||||
|
||||
@@ -50,34 +50,6 @@
|
||||
|
||||
namespace rocr {
|
||||
|
||||
/// @brief: A class behaves as a lock in a scope. When trying to enter into the
|
||||
/// critical section, creat a object of this class. After the control path goes
|
||||
/// out of the scope, it will release the lock automatically.
|
||||
template <class LockType>
|
||||
class ScopedAcquire {
|
||||
public:
|
||||
/// @brief: When constructing, acquire the lock.
|
||||
/// @param: lock(Input), pointer to an existing lock.
|
||||
explicit ScopedAcquire(LockType* lock) : lock_(lock), doRelease(true) { lock_->Acquire(); }
|
||||
|
||||
/// @brief: when destructing, release the lock.
|
||||
~ScopedAcquire() {
|
||||
if (doRelease) lock_->Release();
|
||||
}
|
||||
|
||||
/// @brief: Release the lock early. Avoid using when possible.
|
||||
void Release() {
|
||||
lock_->Release();
|
||||
doRelease = false;
|
||||
}
|
||||
|
||||
private:
|
||||
LockType* lock_;
|
||||
bool doRelease;
|
||||
/// @brief: Disable copiable and assignable ability.
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedAcquire);
|
||||
};
|
||||
|
||||
/// @brief: a class represents a kernel mutex.
|
||||
/// Uses the kernel's scheduler to keep the waiting thread from being scheduled
|
||||
/// until the lock is released (Best for long waits, though anything using
|
||||
@@ -144,6 +116,120 @@ class KernelEvent {
|
||||
DISALLOW_COPY_AND_ASSIGN(KernelEvent);
|
||||
};
|
||||
|
||||
/// @brief: represents a yielding shared mutex.
|
||||
/// aka read/write mutex
|
||||
class KernelSharedMutex {
|
||||
public:
|
||||
/// @brief: Interfaces ScopedAcquire to shared operations.
|
||||
class Shared {
|
||||
public:
|
||||
explicit Shared(KernelSharedMutex* lock) : lock_(lock) {}
|
||||
bool Try() { return lock_->TryShared(); }
|
||||
bool Acquire() { return lock_->AcquireShared(); }
|
||||
void Release() { lock_->ReleaseShared(); }
|
||||
|
||||
private:
|
||||
KernelSharedMutex* lock_;
|
||||
};
|
||||
|
||||
KernelSharedMutex() { lock_ = os::CreateSharedMutex(); }
|
||||
~KernelSharedMutex() { os::DestroySharedMutex(lock_); }
|
||||
|
||||
// Exclusive mode operations
|
||||
bool Try() { return os::TryAcquireSharedMutex(lock_); }
|
||||
bool Acquire() { return os::AcquireSharedMutex(lock_); }
|
||||
void Release() { os::ReleaseSharedMutex(lock_); }
|
||||
|
||||
// Shared mode operations
|
||||
bool TryShared() { return os::TrySharedAcquireSharedMutex(lock_); }
|
||||
bool AcquireShared() { return os::SharedAcquireSharedMutex(lock_); }
|
||||
void ReleaseShared() { os::SharedReleaseSharedMutex(lock_); }
|
||||
|
||||
// Return shared operations interface
|
||||
Shared shared() { return Shared(this); }
|
||||
|
||||
private:
|
||||
os::SharedMutex lock_;
|
||||
|
||||
/// @brief: Disable copiable and assignable ability.
|
||||
DISALLOW_COPY_AND_ASSIGN(KernelSharedMutex);
|
||||
};
|
||||
|
||||
/// @brief: Type trait to identify mutex types
|
||||
template <class T> class isMutex {
|
||||
public:
|
||||
enum { value = false };
|
||||
};
|
||||
template <> class isMutex<KernelMutex> {
|
||||
public:
|
||||
enum { value = true };
|
||||
};
|
||||
template <> class isMutex<SpinMutex> {
|
||||
public:
|
||||
enum { value = true };
|
||||
};
|
||||
template <> class isMutex<KernelSharedMutex> {
|
||||
public:
|
||||
enum { value = true };
|
||||
};
|
||||
|
||||
/// @brief: A class behaves as a lock in a scope. When trying to enter into the
|
||||
/// critical section, creat a object of this class. After the control path goes
|
||||
/// out of the scope, it will release the lock automatically.
|
||||
template <class LockType> class ScopedAcquire {
|
||||
public:
|
||||
/// @brief: When constructing, acquire the lock.
|
||||
/// @param: lock(Input), pointer to an existing lock.
|
||||
explicit ScopedAcquire(LockType* lock) : lock_(lock), doRelease(true) {
|
||||
static_assert(isMutex<LockType>::value, "ScopedAcquire requires a mutex type.");
|
||||
lock_.Acquire();
|
||||
}
|
||||
explicit ScopedAcquire(LockType lock) : lock_(lock), doRelease(true) {
|
||||
static_assert(!isMutex<LockType>::value, "Mutex types are not copyable.");
|
||||
lock_.Acquire();
|
||||
}
|
||||
|
||||
/// @brief: when destructing, release the lock.
|
||||
~ScopedAcquire() {
|
||||
if (doRelease) lock_.Release();
|
||||
}
|
||||
|
||||
/// @brief: Release the lock early. Avoid using when possible.
|
||||
void Release() {
|
||||
lock_.Release();
|
||||
doRelease = false;
|
||||
}
|
||||
|
||||
private:
|
||||
/// @brief: Adapts between pointers to mutex types and mutex pointer types.
|
||||
template <class T, bool B> class container {
|
||||
public:
|
||||
container(T* lock) : lock_(lock) {}
|
||||
__forceinline bool Acquire() { return lock_->Acquire(); }
|
||||
__forceinline void Release() { return lock_->Release(); }
|
||||
|
||||
private:
|
||||
T* lock_;
|
||||
};
|
||||
|
||||
/// @brief: Specialization for mutex pointer types.
|
||||
template <class T> class container<T, false> {
|
||||
public:
|
||||
container(T lock) : lock_(lock) {}
|
||||
__forceinline bool Acquire() { return lock_.Acquire(); }
|
||||
__forceinline void Release() { return lock_.Release(); }
|
||||
|
||||
private:
|
||||
T lock_;
|
||||
};
|
||||
|
||||
container<LockType, isMutex<LockType>::value> lock_;
|
||||
bool doRelease;
|
||||
|
||||
/// @brief: Disable copiable and assignable ability.
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedAcquire);
|
||||
};
|
||||
|
||||
} // namespace rocr
|
||||
|
||||
#endif // HSA_RUNTIME_CORE_SUTIL_LOCKS_H_
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace rocr {
|
||||
namespace os {
|
||||
typedef void* LibHandle;
|
||||
typedef void* Mutex;
|
||||
typedef void* SharedMutex;
|
||||
typedef void* Thread;
|
||||
typedef void* EventHandle;
|
||||
|
||||
@@ -110,6 +111,48 @@ void ReleaseMutex(Mutex lock);
|
||||
/// @return: void.
|
||||
void DestroyMutex(Mutex lock);
|
||||
|
||||
/// @brief: Creates a shared mutex, will return NULL if failed.
|
||||
/// @param: void.
|
||||
/// @return: SharedMutex.
|
||||
SharedMutex CreateSharedMutex();
|
||||
|
||||
/// @brief: Tries to acquire the mutex in exclusive mode once, if successed, return true.
|
||||
/// @param: lock(Input), handle to the shared mutex.
|
||||
/// @return: bool.
|
||||
bool TryAcquireSharedMutex(SharedMutex lock);
|
||||
|
||||
/// @brief: Aquires the mutex in exclusive mode, if the mutex is locked, it will wait until it is
|
||||
/// released. If the mutex is acquired successfully, it will return true.
|
||||
/// @param: lock(Input), handle to the mutex.
|
||||
/// @return: bool.
|
||||
bool AcquireSharedMutex(SharedMutex lock);
|
||||
|
||||
/// @brief: Releases the mutex from exclusive mode.
|
||||
/// @param: lock(Input), handle to the mutex.
|
||||
/// @return: void.
|
||||
void ReleaseSharedMutex(SharedMutex lock);
|
||||
|
||||
/// @brief: Tries to acquire the mutex in shared mode once, if successed, return true.
|
||||
/// @param: lock(Input), handle to the mutex.
|
||||
/// @return: bool.
|
||||
bool TrySharedAcquireSharedMutex(SharedMutex lock);
|
||||
|
||||
/// @brief: Aquires the mutex in shared mode, if the mutex in exclusive mode, it will wait until it
|
||||
/// is released. If the mutex is acquired successfully, it will return true.
|
||||
/// @param: lock(Input), handle to the mutex.
|
||||
/// @return: bool.
|
||||
bool SharedAcquireSharedMutex(SharedMutex lock);
|
||||
|
||||
/// @brief: Releases the mutex from shared mode.
|
||||
/// @param: lock(Input), handle to the mutex.
|
||||
/// @return: void.
|
||||
void SharedReleaseSharedMutex(SharedMutex lock);
|
||||
|
||||
/// @brief: Destroys the mutex.
|
||||
/// @param: lock(Input), handle to the mutex.
|
||||
/// @return: void.
|
||||
void DestroySharedMutex(SharedMutex lock);
|
||||
|
||||
/// @brief: Puts current thread to sleep.
|
||||
/// @param: delayInMs(Input), time in millisecond for sleeping.
|
||||
/// @return: void.
|
||||
|
||||
@@ -224,6 +224,52 @@ uint64_t AccurateClockFrequency() {
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SharedMutex CreateSharedMutex() {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TryAcquireSharedMutex(SharedMutex lock) {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AcquireSharedMutex(SharedMutex lock) {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReleaseSharedMutex(SharedMutex lock) {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
}
|
||||
|
||||
bool TrySharedAcquireSharedMutex(SharedMutex lock) {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedAcquireSharedMutex(SharedMutex lock) {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
void SharedReleaseSharedMutex(SharedMutex lock) {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
}
|
||||
|
||||
void DestroySharedMutex(SharedMutex lock) {
|
||||
assert(false && "Not implemented.");
|
||||
abort();
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
} // namespace rocr
|
||||
|
||||
|
||||
Référencer dans un nouveau ticket
Bloquer un utilisateur