Files
TheRock/rocm-systems/projects/hip-tests/catch/unit/memory/mempool_common.hh
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

875 lines
28 KiB
C++

/*
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <hip_test_common.hh>
#include <hip_test_kernels.hh>
#include <hip_test_process.hh>
#include <resource_guards.hh>
#include <utils.hh>
#include <chrono>
#include <thread>
namespace {
constexpr auto wait_ms = 500;
} // anonymous namespace
#define checkMempoolSupported(device) { \
int deviceSupportsMemoryPools = 0; \
HIP_CHECK(hipDeviceGetAttribute(&deviceSupportsMemoryPools, \
hipDeviceAttributeMemoryPoolsSupported, device)); \
if (0 == deviceSupportsMemoryPools) { \
HIP_SKIP_TEST(HipTest::SkipReason::kMemoryPoolUnsupported); \
} \
}
#define checkIfMultiDev(numOfDev) { \
if (numOfDev < 2) { \
HIP_SKIP_TEST(HipTest::SkipReason::kFewerThanTwoGpus); \
} \
}
template <typename T> __global__ void kernel_500ms(T* host_res, int clk_rate) {
int tid = threadIdx.x + blockIdx.x * blockDim.x;
host_res[tid] = tid + 1;
__threadfence_system();
// expecting that the data is getting flushed to host here!
uint64_t start = clock64() / clk_rate, cur;
if (clk_rate > 1) {
do {
cur = clock64() / clk_rate - start;
} while (cur < wait_ms);
} else {
do {
cur = clock64() / start;
} while (cur < wait_ms);
}
}
template <typename T> __global__ void kernel_500ms_gfx11(T* host_res, int clk_rate) {
#if HT_AMD
int tid = threadIdx.x + blockIdx.x * blockDim.x;
host_res[tid] = tid + 1;
__threadfence_system();
// expecting that the data is getting flushed to host here!
uint64_t start = clock_function() / clk_rate, cur;
if (clk_rate > 1) {
do {
cur = clock_function() / clk_rate - start;
} while (cur < wait_ms);
} else {
do {
cur = clock_function() / start;
} while (cur < wait_ms);
}
#endif
}
template <typename T> __global__ void notifiedKernel(T* host_res, volatile unsigned int* notified) {
int tid = threadIdx.x + blockIdx.x * blockDim.x;
host_res[tid] = tid + 1;
if (threadIdx.x == 0) {
while (*notified == 0) { }
}
}
template <typename F> void MallocMemPoolAsync_OneAlloc(F malloc_func, const MemPools mempool_type) {
int device_id = 0;
HIP_CHECK(hipSetDevice(device_id));
int mem_pool_support = 0;
HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0));
if (!mem_pool_support) {
HIP_SKIP_TEST(HipTest::SkipReason::kMemoryPoolUnsupported);
}
unsigned int *notified = nullptr;
HIP_CHECK(hipHostMalloc(&notified, sizeof(unsigned int)));
*notified = 0;
const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, allocation_size);
MemPoolGuard mempool(mempool_type, device_id);
int* alloc_mem;
StreamGuard stream(Streams::created);
HIP_CHECK(malloc_func(reinterpret_cast<void**>(&alloc_mem), allocation_size, mempool.mempool(),
stream.stream()));
int blocks = 16;
hipMemPoolAttr attr;
notifiedKernel<<<blocks, 32, 0, stream.stream()>>>(alloc_mem, notified);
const auto element_count = allocation_size / sizeof(int);
constexpr auto thread_count = 1024;
const auto block_count = element_count / thread_count + 1;
constexpr int expected_value = 17;
VectorSet<<<block_count, thread_count, 0, stream.stream()>>>(alloc_mem, expected_value,
element_count);
HIP_CHECK(hipMemcpyAsync(host_alloc.host_ptr(), alloc_mem, allocation_size, hipMemcpyDeviceToHost,
stream.stream()));
HIP_CHECK(hipFreeAsync(reinterpret_cast<void*>(alloc_mem), stream.stream()));
attr = hipMemPoolAttrReservedMemCurrent;
std::uint64_t res_before_sync = 0;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_before_sync));
*notified = 1;
HIP_CHECK(hipStreamSynchronize(stream.stream()));
std::uint64_t res_after_sync = 0;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_after_sync));
// Sync must release memory to OS
REQUIRE(res_after_sync <= res_before_sync);
std::uint64_t used_mem = 10;
attr = hipMemPoolAttrUsedMemCurrent;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem));
REQUIRE(0 == used_mem);
ArrayFindIfNot(host_alloc.host_ptr(), expected_value, element_count);
HIP_CHECK(hipHostFree(notified));
}
template <typename F>
void MallocMemPoolAsync_TwoAllocs(F malloc_func, const MemPools mempool_type) {
int device_id = 0;
HIP_CHECK(hipSetDevice(device_id));
int mem_pool_support = 0;
HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0));
if (!mem_pool_support) {
HIP_SKIP_TEST(HipTest::SkipReason::kMemoryPoolUnsupported);
}
unsigned int *notified = nullptr;
HIP_CHECK(hipHostMalloc(&notified, sizeof(unsigned int)));
*notified = 0;
const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, allocation_size);
MemPoolGuard mempool(mempool_type, device_id);
int* alloc_mem1;
int* alloc_mem2;
StreamGuard stream(Streams::created);
HIP_CHECK(malloc_func(reinterpret_cast<void**>(&alloc_mem1), allocation_size, mempool.mempool(),
stream.stream()));
HIP_CHECK(malloc_func(reinterpret_cast<void**>(&alloc_mem2), allocation_size, mempool.mempool(),
stream.stream()));
int blocks = 16;
hipMemPoolAttr attr;
notifiedKernel<<<blocks, 32, 0, stream.stream()>>>(alloc_mem1, notified);
const auto element_count = allocation_size / sizeof(int);
constexpr auto thread_count = 1024;
const auto block_count = element_count / thread_count + 1;
constexpr int expected_value = 17;
VectorSet<<<block_count, thread_count, 0, stream.stream()>>>(alloc_mem1, expected_value,
element_count);
HIP_CHECK(hipGetLastError());
HIP_CHECK(hipMemcpyAsync(alloc_mem2, alloc_mem1, allocation_size, hipMemcpyDeviceToDevice,
stream.stream()));
HIP_CHECK(hipMemcpyAsync(host_alloc.host_ptr(), alloc_mem2, allocation_size,
hipMemcpyDeviceToHost, stream.stream()));
HIP_CHECK(hipFreeAsync(reinterpret_cast<void*>(alloc_mem1), stream.stream()));
attr = hipMemPoolAttrReservedMemCurrent;
std::uint64_t res_before_sync = 0;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_before_sync));
*notified = 1;
HIP_CHECK(hipStreamSynchronize(stream.stream()));
std::uint64_t res_after_sync = 0;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &res_after_sync));
// Sync must release memory to OS
REQUIRE(res_after_sync <= res_before_sync);
std::uint64_t used_mem = 0;
attr = hipMemPoolAttrUsedMemCurrent;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem));
// Make sure the current usage query works - just second buffer is left
REQUIRE(allocation_size == used_mem);
attr = hipMemPoolAttrUsedMemHigh;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem));
// Make sure the high watermark usage works - both buffers must be reported
REQUIRE((2 * allocation_size) == used_mem);
HIP_CHECK(hipFreeAsync(reinterpret_cast<void*>(alloc_mem2), stream.stream()));
HIP_CHECK(hipStreamSynchronize(stream.stream()));
attr = hipMemPoolAttrUsedMemCurrent;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &used_mem));
// Make sure the current usage query works - none of the buffers are used
REQUIRE(0 == used_mem);
ArrayFindIfNot(host_alloc.host_ptr(), expected_value, element_count);
HIP_CHECK(hipHostFree(notified));
}
template <typename F> void MallocMemPoolAsync_Reuse(F malloc_func, const MemPools mempool_type) {
int device_id = 0;
HIP_CHECK(hipSetDevice(device_id));
int mem_pool_support = 0;
HIP_CHECK(hipDeviceGetAttribute(&mem_pool_support, hipDeviceAttributeMemoryPoolsSupported, 0));
if (!mem_pool_support) {
HIP_SKIP_TEST(HipTest::SkipReason::kMemoryPoolUnsupported);
}
unsigned int *notified = nullptr;
HIP_CHECK(hipHostMalloc(&notified, sizeof(unsigned int)));
*notified = 0;
MemPoolGuard mempool(mempool_type, device_id);
int *alloc_mem1, *alloc_mem2, *alloc_mem3;
StreamGuard stream(Streams::created);
size_t allocation_size1 = kPageSize * kPageSize * 2;
HIP_CHECK(malloc_func(reinterpret_cast<void**>(&alloc_mem1), allocation_size1, mempool.mempool(),
stream.stream()));
size_t allocation_size2 = kPageSize;
HIP_CHECK(malloc_func(reinterpret_cast<void**>(&alloc_mem3), allocation_size2, mempool.mempool(),
stream.stream()));
int blocks = 2;
notifiedKernel<<<blocks, 32, 0, stream.stream()>>>(alloc_mem1, notified);
hipMemPoolAttr attr;
// Not a real free, since kernel isn't done
HIP_CHECK(hipFreeAsync(reinterpret_cast<void*>(alloc_mem1), stream.stream()));
HIP_CHECK(malloc_func(reinterpret_cast<void**>(&alloc_mem2), allocation_size1, mempool.mempool(),
stream.stream()));
// Runtime must reuse the pointer
REQUIRE(alloc_mem1 == alloc_mem2);
// Make a sync before the second kernel launch to make sure memory B isn't gone
*notified = 1;
HIP_CHECK(hipStreamSynchronize(stream.stream()));
*notified = 0;
// Second kernel launch with new memory
notifiedKernel<<<blocks, 32, 0, stream.stream()>>>(alloc_mem2, notified);
*notified = 1;
HIP_CHECK(hipStreamSynchronize(stream.stream()));
attr = hipMemPoolAttrUsedMemCurrent;
std::uint64_t value64 = 0;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64));
// Make sure the current usage reports the both buffers
REQUIRE((allocation_size1 + allocation_size2) == value64);
attr = hipMemPoolAttrUsedMemHigh;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64));
// Make sure the high watermark usage works - the both buffers must be reported
REQUIRE((allocation_size1 + allocation_size2) == value64);
HIP_CHECK(hipFreeAsync(reinterpret_cast<void*>(alloc_mem2), stream.stream()));
attr = hipMemPoolAttrUsedMemCurrent;
HIP_CHECK(hipMemPoolGetAttribute(mempool.mempool(), attr, &value64));
// Make sure the current usage reports just one buffer, because the above free doesn't hold memory
REQUIRE(allocation_size2 == value64);
HIP_CHECK(hipFreeAsync(reinterpret_cast<void*>(alloc_mem3), stream.stream()));
HIP_CHECK(hipHostFree(notified));
}
// definitions
#define THREADS_PER_BLOCK 512
#define LAUNCH_ITERATIONS 5
#define NUMBER_OF_THREADS 5
#define NUM_OF_STREAM 3
enum eTestValue {
testdefault,
testMaximum,
testDisabled,
testEnabled
};
class streamMemAllocTest {
int *A_h, *B_h, *C_h;
int *A_d, *B_d, *C_d;
int size;
size_t byte_size;
hipMemPool_t mem_pool;
bool threadSafe;
public:
explicit streamMemAllocTest(int N, bool threadSafe = false)
: size(N), threadSafe(threadSafe) {
byte_size = N*sizeof(int);
}
// Create host buffers and initialize them with input data
void createHostBufferWithData() {
A_h = reinterpret_cast<int*>(malloc(byte_size));
REQUIRE_OPT_THREAD(threadSafe, A_h != nullptr);
B_h = reinterpret_cast<int*>(malloc(byte_size));
REQUIRE_OPT_THREAD(threadSafe, B_h != nullptr);
C_h = reinterpret_cast<int*>(malloc(byte_size));
REQUIRE_OPT_THREAD(threadSafe, C_h != nullptr);
// set data to host
for (int i = 0; i < size; i++) {
A_h[i] = 2*i + 1; // Odd
B_h[i] = 2*i; // Even
C_h[i] = 0;
}
}
// Instead of creating a mempool in class use the global mempool.
void useCommonMempool(hipMemPool_t mempool) {
mem_pool = mempool;
}
// Create the mempool
void createMempool(hipMemPoolAttr attr, enum eTestValue testtype,
int dev) {
// Create mempool in current device
hipMemPoolProps pool_props{};
pool_props.allocType = hipMemAllocationTypePinned;
pool_props.location.id = dev;
pool_props.location.type = hipMemLocationTypeDevice;
HIP_CHECK_OPT_THREAD(threadSafe, hipMemPoolCreate(&mem_pool, &pool_props));
if (attr == hipMemPoolAttrReleaseThreshold) {
uint64_t setThreshold = 0;
if (testtype == testMaximum) {
setThreshold = UINT64_MAX;
}
HIP_CHECK_OPT_THREAD(threadSafe, hipMemPoolSetAttribute(mem_pool, attr, &setThreshold));
} else if ((attr == hipMemPoolReuseFollowEventDependencies) ||
(attr == hipMemPoolReuseAllowOpportunistic) ||
(attr == hipMemPoolReuseAllowInternalDependencies)) {
int value = 0;
if (testtype == testEnabled) {
value = 1;
}
HIP_CHECK_OPT_THREAD(threadSafe, hipMemPoolSetAttribute(mem_pool, attr, &value));
}
}
// allocate device memory from mempool.
void allocFromMempool(hipStream_t stream) {
HIP_CHECK_OPT_THREAD(threadSafe, hipMallocFromPoolAsync(reinterpret_cast<void**>(&A_d),
byte_size, mem_pool, stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipMallocFromPoolAsync(reinterpret_cast<void**>(&B_d),
byte_size, mem_pool, stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipMallocFromPoolAsync(reinterpret_cast<void**>(&C_d),
byte_size, mem_pool, stream));
}
// Transfer data from host to device asynchronously.
void transferToMempool(hipStream_t stream) {
HIP_CHECK_OPT_THREAD(threadSafe, hipMemcpyAsync(A_d, A_h, byte_size, hipMemcpyHostToDevice,
stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipMemcpyAsync(B_d, B_h, byte_size, hipMemcpyHostToDevice,
stream));
}
// allocate from default mempool.
void allocFromDefMempool(hipStream_t stream) {
HIP_CHECK_OPT_THREAD(threadSafe, hipMallocAsync(reinterpret_cast<void**>(&A_d),
byte_size, stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipMallocAsync(reinterpret_cast<void**>(&B_d),
byte_size, stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipMallocAsync(reinterpret_cast<void**>(&C_d),
byte_size, stream));
}
// Execute Kernel to process input data and wait for it.
void runKernel(hipStream_t stream) {
int blocks = (size % THREADS_PER_BLOCK == 0) ? (size / THREADS_PER_BLOCK)
: ((size / THREADS_PER_BLOCK) + 1);
hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(THREADS_PER_BLOCK), 0, stream,
static_cast<const int*>(A_d), static_cast<const int*>(B_d), C_d, size);
HIP_CHECK_OPT_THREAD(threadSafe, hipGetLastError());
}
// Transfer data from device to host asynchronously.
void transferFromMempool(hipStream_t stream) {
HIP_CHECK_OPT_THREAD(threadSafe, hipMemcpyAsync(C_h, C_d, byte_size, hipMemcpyDeviceToHost,
stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipStreamSynchronize(stream));
}
// Validate the data returned from device.
bool validateResult() {
for (int i = 0; i < size; i++) {
auto res = A_h[i] + B_h[i];
REQUIRE(res == C_h[i]);
}
return true;
}
// Thread-safe variant of validateResult(): returns a bool instead of using a
// Catch2 macro, so it is safe to call from worker threads.
bool validateResultThreadSafe() {
for (int i = 0; i < size; i++) {
auto res = A_h[i] + B_h[i];
if (res != C_h[i]) {
return false;
}
}
return true;
}
// Free device memory
void freeDevBuf(hipStream_t stream) {
HIP_CHECK_OPT_THREAD(threadSafe, hipFreeAsync(reinterpret_cast<void*>(A_d), stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipFreeAsync(reinterpret_cast<void*>(B_d), stream));
HIP_CHECK_OPT_THREAD(threadSafe, hipFreeAsync(reinterpret_cast<void*>(C_d), stream));
}
// Free mempool if not using global mempool
void freeMempool() {
HIP_CHECK_OPT_THREAD(threadSafe, hipMemPoolDestroy(mem_pool));
}
// Free all host buffers
void freeHostBuf() {
free(A_h);
free(B_h);
free(C_h);
}
};
#define checkSysCallErrors(result) \
if (result == -1) { \
fprintf(stderr, "Failure at %u %s\n", __LINE__, __FILE__); exit(EXIT_FAILURE); \
}
#if HT_WIN
typedef HANDLE hipShareableHdl;
#else
#ifdef HT_AMD
typedef int64_t hipShareableHdl;
#else
typedef int hipShareableHdl;
#endif
#endif
class SharedMemory {
void* addr_ = nullptr;
size_t size_ = 0;
#if HT_WIN
HANDLE shmHandle_ = nullptr;
#else
int shmFd_ = -1;
#endif
bool opened_ = false;
public:
SharedMemory() = default;
~SharedMemory() { close(); }
SharedMemory(const SharedMemory&) = delete;
SharedMemory& operator=(const SharedMemory&) = delete;
int create(const char* name, size_t sz) {
#if HT_WIN
size_ = sz;
shmHandle_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, (DWORD)sz, name);
if (shmHandle_ == 0) return GetLastError();
addr_ = MapViewOfFile(shmHandle_, FILE_MAP_ALL_ACCESS, 0, 0, sz);
if (addr_ == NULL) {
close();
return GetLastError();
}
#else
size_ = sz;
shmFd_ = shm_open(name, O_RDWR | O_CREAT, 0777);
if (shmFd_ < 0) return errno;
if (ftruncate(shmFd_, sz) != 0) {
close();
return errno;
}
addr_ = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, shmFd_, 0);
if (addr_ == MAP_FAILED) {
addr_ = nullptr;
close();
return errno;
}
#endif
opened_ = true;
return 0;
}
int open(const char* name, size_t sz) {
#if HT_WIN
size_ = sz;
shmHandle_ = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name);
if (shmHandle_ == 0) return GetLastError();
addr_ = MapViewOfFile(shmHandle_, FILE_MAP_ALL_ACCESS, 0, 0, sz);
if (addr_ == NULL) {
close();
return GetLastError();
}
#else
size_ = sz;
shmFd_ = shm_open(name, O_RDWR, 0777);
if (shmFd_ < 0) {
close();
return errno;
}
addr_ = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, shmFd_, 0);
if (addr_ == MAP_FAILED) {
addr_ = nullptr;
close();
return errno;
}
#endif
opened_ = true;
return 0;
}
void close() {
#if HT_WIN
if (addr_) { UnmapViewOfFile(addr_); addr_ = nullptr; }
if (shmHandle_) { CloseHandle(shmHandle_); shmHandle_ = nullptr; }
#else
if (addr_) { munmap(addr_, size_); addr_ = nullptr; }
if (shmFd_ >= 0) { ::close(shmFd_); shmFd_ = -1; }
#endif
size_ = 0;
opened_ = false;
}
void* addr() const { return addr_; }
size_t size() const { return size_; }
template <typename T>
T* as() { return reinterpret_cast<T*>(addr_); }
};
inline void barrierWait(std::atomic<int>& barrier, std::atomic<int>& sense, unsigned int n,
std::chrono::seconds timeout = std::chrono::seconds(10)) {
int count = barrier.fetch_add(1, std::memory_order_acq_rel) + 1;
if (static_cast<unsigned int>(count) == n) {
barrier.store(0, std::memory_order_release);
int current_sense = sense.load(std::memory_order_relaxed);
sense.store(1 - current_sense, std::memory_order_release);
} else {
int old_sense = sense.load(std::memory_order_relaxed);
auto deadline = std::chrono::steady_clock::now() + timeout;
while (sense.load(std::memory_order_acquire) == old_sense) {
if (std::chrono::steady_clock::now() >= deadline) {
WARN("barrierWait timed out after " << timeout.count() << "s waiting for "
<< n << " threads (only " << count << " arrived)");
break;
}
std::this_thread::yield();
}
}
}
struct mempoolIpcShmStruct {
hipMemPoolPtrExportData ptrExportData;
hipMemAllocationHandleType handleType;
int device;
std::atomic<int> barrier;
std::atomic<int> sense;
};
struct ipcHdl {
#if HT_WIN
HANDLE mailslot;
#else
int socket;
#endif
char *name;
};
class ipcSocketCom {
ipcHdl *handle;
int createSocket() {
#if HT_WIN
handle = new ipcHdl;
if (nullptr == handle) {
perror("Socket failure: Handle memory allocation failed");
return -1;
}
handle->mailslot = INVALID_HANDLE_VALUE;
handle->name = NULL;
return 0;
#else
int server_fd;
struct sockaddr_un servaddr;
char name[16];
sprintf(name, "%u", getpid());
handle = new ipcHdl;
if (nullptr == handle) {
perror("Socket failure: Handle memory allocation failed");
return -1;
}
memset(handle, 0, sizeof(*handle));
handle->socket = -1;
handle->name = NULL;
if ((server_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("Socket failure: Socket creation failed");
return -1;
}
unlink(name);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
size_t len = strlen(name);
if (len > (sizeof(servaddr.sun_path) - 1)) {
perror("Socket failure: Cannot bind provided name to socket. Name too large");
return -1;
}
strncpy(servaddr.sun_path, name, len);
if (bind(server_fd, (struct sockaddr *)&servaddr, SUN_LEN(&servaddr)) < 0) {
perror("Socket failure: Binding socket failed");
return -1;
}
handle->name = new char[strlen(name) + 1];
strcpy(handle->name, name);
handle->socket = server_fd;
return 0;
#endif
}
int openSocket() {
#if HT_WIN
handle = new ipcHdl;
if (nullptr == handle) {
perror("Socket failure: Handle memory allocation failed");
return -1;
}
memset(handle, 0, sizeof(*handle));
handle->mailslot = INVALID_HANDLE_VALUE;
handle->name = nullptr;
char name[128];
sprintf(name, "\\\\.\\mailslot\\hipMemPoolIPC_%lu",
(unsigned long)GetCurrentProcessId());
handle->mailslot = CreateMailslot(name, 0, MAILSLOT_WAIT_FOREVER, NULL);
if (handle->mailslot == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateMailslot failed (%lu)\n", GetLastError());
if (handle->name) delete[] handle->name;
delete handle;
handle = nullptr;
return -1;
}
handle->name = new char[strlen(name) + 1];
strcpy(handle->name, name);
return 0;
#else
int sock = 0;
struct sockaddr_un cliaddr;
handle = new ipcHdl;
if (nullptr == handle) {
perror("Socket failure: Handle memory allocation failed");
return -1;
}
memset(handle, 0, sizeof(*handle));
if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("IPC failure:Socket creation error");
return -1;
}
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_UNIX;
char name[16];
sprintf(name, "%u", getpid());
strcpy(cliaddr.sun_path, name);
if (bind(sock, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) {
perror("Socket failure: Binding socket failed");
return -1;
}
handle->socket = sock;
handle->name = new char[strlen(name) + 1];
strcpy(handle->name, name);
return 0;
#endif
}
int closeSocket() {
#if HT_WIN
if (!handle) return -1;
if (handle->mailslot != INVALID_HANDLE_VALUE) {
CloseHandle(handle->mailslot);
}
if (handle->name) delete[] handle->name;
delete handle;
handle = nullptr;
return 0;
#else
if (!handle) {
return -1;
}
if (handle->name) {
unlink(handle->name);
delete[] handle->name;
}
close(handle->socket);
delete handle;
return 0;
#endif
}
public:
ipcSocketCom() = default;
ipcSocketCom(bool isServer) {
if (isServer) {
checkSysCallErrors(createSocket());
} else {
checkSysCallErrors(openSocket());
}
}
~ipcSocketCom() {
}
int closeThisSock() {
return closeSocket();
}
int recvShareableHdl(hipShareableHdl *shHandle) {
#if HT_WIN
DWORD cbRead = 0;
if (!ReadFile(handle->mailslot, shHandle, sizeof(*shHandle), &cbRead, NULL)) {
fprintf(stderr, "ReadFile failed (%lu)\n", GetLastError());
return -1;
}
if (cbRead != sizeof(*shHandle)) {
fprintf(stderr, "ReadFile returned unexpected size (%lu, expected %zu)\n",
cbRead, sizeof(*shHandle));
return -1;
}
return 0;
#else
struct msghdr msg = {};
struct iovec iov[1];
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un = {};
struct cmsghdr *cmptr;
ssize_t n;
int receivedfd;
int dummy_data;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
iov[0].iov_base = &dummy_data;
iov[0].iov_len = sizeof(dummy_data);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ((n = recvmsg(handle->socket, &msg, 0)) <= 0) {
perror("Socket failure: Receiving data over socket failed");
return -1;
}
if (((cmptr = CMSG_FIRSTHDR(&msg)) != NULL) &&
(cmptr->cmsg_len == CMSG_LEN(sizeof(int)))) {
if ((cmptr->cmsg_level != SOL_SOCKET) || (cmptr->cmsg_type != SCM_RIGHTS)) {
return -1;
}
memmove(&receivedfd, CMSG_DATA(cmptr), sizeof(receivedfd));
*(int *)shHandle = receivedfd;
} else {
return -1;
}
return 0;
#endif
}
int sendShareableHdl(hipShareableHdl shareableHdl, Process process) {
#if HT_WIN
HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, process.dwProcessId);
if (hProcess == NULL) {
fprintf(stderr, "OpenProcess failed (%lu)\n", GetLastError());
return -1;
}
HANDLE hDup = INVALID_HANDLE_VALUE;
if (!DuplicateHandle(GetCurrentProcess(), shareableHdl, hProcess,
&hDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
CloseHandle(hProcess);
fprintf(stderr, "DuplicateHandle failed (%lu)\n", GetLastError());
return -1;
}
CloseHandle(hProcess);
char slotName[128];
sprintf(slotName, "\\\\.\\mailslot\\hipMemPoolIPC_%lu",
(unsigned long)process.dwProcessId);
HANDLE hFile = CreateFile(slotName, GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFile for mailslot failed (%lu)\n", GetLastError());
return -1;
}
DWORD cbWritten;
if (!WriteFile(hFile, &hDup, sizeof(hDup), &cbWritten, NULL)) {
CloseHandle(hFile);
fprintf(stderr, "WriteFile failed (%lu)\n", GetLastError());
return -1;
}
CloseHandle(hFile);
return 0;
#else
struct msghdr msg = {};
struct iovec iov[1];
int dummy_data = 0;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un = {};
struct cmsghdr *cmptr;
struct sockaddr_un cliaddr;
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_UNIX;
strcpy(cliaddr.sun_path, std::to_string(process).c_str());
int sendfd = (int)shareableHdl;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
memmove(CMSG_DATA(cmptr), &sendfd, sizeof(sendfd));
msg.msg_name = (void *)&cliaddr;
msg.msg_namelen = sizeof(struct sockaddr_un);
iov[0].iov_base = &dummy_data;
iov[0].iov_len = sizeof(dummy_data);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
ssize_t sendResult = sendmsg(handle->socket, &msg, 0);
if (sendResult <= 0) {
perror("Socket failure: Sending data over socket failed");
return -1;
}
return 0;
#endif
}
};