Skip to content
3 changes: 2 additions & 1 deletion unified-runtime/source/adapters/level_zero/v2/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,8 @@ ur_discrete_buffer_handle_t::ur_discrete_buffer_handle_t(
}

ur_discrete_buffer_handle_t::~ur_discrete_buffer_handle_t() {
if (!activeAllocationDevice || !writeBackPtr)
if (!activeAllocationDevice || !activeAllocationDevice->Id.has_value() ||
!writeBackPtr)
return;

auto srcPtr = getActiveDeviceAlloc();
Expand Down
23 changes: 12 additions & 11 deletions unified-runtime/source/adapters/opencl/command_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,10 @@ ur_exp_command_buffer_handle_t_::~ur_exp_command_buffer_handle_t_() {
clReleaseEvent(LastSubmission);
}

cl_context CLContext = hContext->CLContext;
cl_ext::clReleaseCommandBufferKHR_fn clReleaseCommandBufferKHR = nullptr;
cl_int Res =
cl_ext::getExtFuncFromContext<decltype(clReleaseCommandBufferKHR)>(
CLContext,
cast(ur::cl::getAdapter())->fnCache.clReleaseCommandBufferKHRCache,
cl_ext::ReleaseCommandBufferName, &clReleaseCommandBufferKHR);
assert(Res == CL_SUCCESS);
(void)Res;

clReleaseCommandBufferKHR(CLCommandBuffer);
if (CLCommandBuffer) {
[[maybe_unused]] cl_int Res = CLReleaseCommandBufferKHR(CLCommandBuffer);
assert(Res == CL_SUCCESS);
}
}

ur_result_t
Expand All @@ -54,6 +47,12 @@ urCommandBufferCreateExp(ur_context_handle_t hContext,
CLContext,
cast(ur::cl::getAdapter())->fnCache.clCreateCommandBufferKHRCache,
cl_ext::CreateCommandBufferName, &clCreateCommandBufferKHR));
cl_ext::clReleaseCommandBufferKHR_fn clReleaseCommandBufferKHR = nullptr;
UR_RETURN_ON_FAILURE(
cl_ext::getExtFuncFromContext<decltype(clReleaseCommandBufferKHR)>(
CLContext,
cast(ur::cl::getAdapter())->fnCache.clReleaseCommandBufferKHRCache,
cl_ext::ReleaseCommandBufferName, &clReleaseCommandBufferKHR));

const bool IsUpdatable = pCommandBufferDesc->isUpdatable;

Expand Down Expand Up @@ -102,6 +101,8 @@ urCommandBufferCreateExp(ur_context_handle_t hContext,
auto URCommandBuffer = std::make_unique<ur_exp_command_buffer_handle_t_>(
QueuePtr, cast(hContext), cast(hDevice), CLCommandBuffer, IsUpdatable,
IsInOrder);
URCommandBuffer->CLCreateCommandBufferKHR = clCreateCommandBufferKHR;
URCommandBuffer->CLReleaseCommandBufferKHR = clReleaseCommandBufferKHR;
*phCommandBuffer = cast(URCommandBuffer.release());
} catch (std::bad_alloc &) {
return UR_RESULT_ERROR_OUT_OF_RESOURCES;
Expand Down
5 changes: 5 additions & 0 deletions unified-runtime/source/adapters/opencl/command_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ struct ur_exp_command_buffer_handle_t_ : handle_base {
ur_device_handle_t_ *hDevice;
/// OpenCL command-buffer object.
cl_command_buffer_khr CLCommandBuffer;
/// OpenCL function used to create the command-buffer object.
cl_ext::clCreateCommandBufferKHR_fn CLCreateCommandBufferKHR;
/// OpenCL function used to release the command-buffer object.
cl_ext::clReleaseCommandBufferKHR_fn CLReleaseCommandBufferKHR;
/// Set to true if the kernel commands in the command-buffer can be updated,
/// false otherwise
bool IsUpdatable;
Expand All @@ -67,6 +71,7 @@ struct ur_exp_command_buffer_handle_t_ : handle_base {
bool IsUpdatable, bool IsInOrder)
: handle_base(), hInternalQueue(hQueue), hContext(hContext),
hDevice(hDevice), CLCommandBuffer(CLCommandBuffer),
CLCreateCommandBufferKHR(nullptr), CLReleaseCommandBufferKHR(nullptr),
IsUpdatable(IsUpdatable), IsInOrder(IsInOrder), IsFinalized(false),
LastSubmission(nullptr) {}

Expand Down
6 changes: 5 additions & 1 deletion unified-runtime/source/adapters/opencl/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ struct ur_context_handle_t_ : handle_base {
// clear the ext function pointer cache. This isn't foolproof sadly but it
// should drastically reduce the chances of the pathological case described
// in the comments in common.hpp.
cast(ur::cl::getAdapter())->fnCache.clearCache(CLContext);
try {
cast(ur::cl::getAdapter())->fnCache.clearCache(CLContext);
} catch (...) {
assert(false && "Failed to clear OpenCL extension function cache");
}

for (uint32_t i = 0; i < DeviceCount; i++) {
ur::opencl::urDeviceRelease(cast(Devices[i]));
Expand Down
8 changes: 4 additions & 4 deletions unified-runtime/source/adapters/opencl/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ struct ur_device_handle_t_ : handle_base {

~ur_device_handle_t_() {
if (ParentDevice) {
// This does not need protected by a lock; this destructor can only run
// exactly once. However, to prevent issues with the OpenCL handle being
// reused, CLDevice must still be alive here.
Platform->SubDevices.erase(CLDevice);
{
std::lock_guard lock{Platform->SubDevicesLock};
Platform->SubDevices.erase(CLDevice);
}
Comment on lines +46 to +49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was the previous comment incorrect?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverity claims that there could be a race condition here if another thread concurrently performs a lookup or insertion

[[maybe_unused]] auto Res = clReleaseDevice(CLDevice);
assert(Res == CL_SUCCESS);
}
Expand Down
22 changes: 4 additions & 18 deletions unified-runtime/source/adapters/opencl/enqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,25 +546,8 @@ ur_result_t urEnqueueKernelLaunchWithArgsExp(
_launchPropList->pNext);
}

// Only look up USM function pointer if we have POINTER args
clSetKernelArgMemPointerINTEL_fn SetKernelArgMemPointerPtr = nullptr;
bool hasPointerArgs = false;
for (uint32_t i = 0; i < numArgs; i++) {
if (pArgs[i].type == UR_EXP_KERNEL_ARG_TYPE_POINTER) {
hasPointerArgs = true;
break;
}
}
auto Queue = cast(hQueue);
auto Kernel = cast(hKernel);
if (hasPointerArgs) {
UR_RETURN_ON_FAILURE(
cl_ext::getExtFuncFromContext<clSetKernelArgMemPointerINTEL_fn>(
Queue->Context->CLContext,
cast(ur::cl::getAdapter())
->fnCache.clSetKernelArgMemPointerINTELCache,
cl_ext::SetKernelArgMemPointerName, &SetKernelArgMemPointerPtr));
}

for (uint32_t i = 0; i < numArgs; i++) {
switch (pArgs[i].type) {
Expand All @@ -588,7 +571,10 @@ ur_result_t urEnqueueKernelLaunchWithArgsExp(
break;
}
case UR_EXP_KERNEL_ARG_TYPE_POINTER:
CL_RETURN_ON_FAILURE(SetKernelArgMemPointerPtr(
if (!Kernel->clSetKernelArgMemPointerINTEL) {
return UR_RESULT_ERROR_UNSUPPORTED_FEATURE;
}
CL_RETURN_ON_FAILURE(Kernel->clSetKernelArgMemPointerINTEL(
Kernel->CLKernel, static_cast<cl_uint>(pArgs[i].index),
pArgs[i].value.pointer));
break;
Expand Down
2 changes: 1 addition & 1 deletion unified-runtime/source/adapters/opencl/event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ inline ur_result_t createUREvent(cl_event Event, ur_context_handle_t Context,
try {
auto UREvent =
std::make_unique<ur_event_handle_t_>(Event, cast(Context), UrQueue);
UR_RETURN_ON_FAILURE(UrQueue->storeLastEvent(cast(UREvent.get())));
*ReturnedEvent = cast(UREvent.release());
UR_RETURN_ON_FAILURE(UrQueue->storeLastEvent(*ReturnedEvent));
} catch (std::bad_alloc &) {
return UR_RESULT_ERROR_OUT_OF_RESOURCES;
} catch (...) {
Expand Down
5 changes: 1 addition & 4 deletions unified-runtime/source/adapters/opencl/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,9 @@ ur_result_t urPlatformGet(ur_adapter_handle_t, uint32_t NumEntries,
}

auto Adapter = cast(AdapterHandle);
if (Adapter->NumPlatforms == 0) {
{
std::lock_guard guard{adapterPopulationMutex};

// It's possible for urPlatformGet, if ran on multiple threads, to enter
// this branch simultaneously. This check ensures that only one sees that
// Adapter->NumPlatforms is zero.
if (Adapter->NumPlatforms == 0) {
uint32_t NumPlatforms = 0;
cl_int Res = clGetPlatformIDs(0, nullptr, &NumPlatforms);
Expand Down
21 changes: 17 additions & 4 deletions unified-runtime/source/adapters/opencl/usm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,14 +341,14 @@ ur_result_t urEnqueueUSMFill(ur_queue_handle_t hQueue, void *ptr,
numEventsInWaitList, CLWaitEvents.data(),
&CopyEvent));

std::unique_ptr<ur_event_handle_t_> UREvent;
if (phEvent) {
// Since we're releasing this in the callback above we need to retain it
// here to keep the user copy alive.
CL_RETURN_ON_FAILURE(clRetainEvent(CopyEvent));
try {
auto UREvent = std::make_unique<ur_event_handle_t_>(
CopyEvent, Queue->Context, Queue);
*phEvent = cast(UREvent.release());
UREvent = std::make_unique<ur_event_handle_t_>(CopyEvent, Queue->Context,
Queue);
} catch (std::bad_alloc &) {
return UR_RESULT_ERROR_OUT_OF_RESOURCES;
} catch (...) {
Expand All @@ -372,6 +372,10 @@ ur_result_t urEnqueueUSMFill(ur_queue_handle_t hQueue, void *ptr,
CL_RETURN_ON_FAILURE(ClErr);
}

if (phEvent) {
*phEvent = cast(UREvent.release());
}

return UR_RESULT_SUCCESS;
}

Expand Down Expand Up @@ -506,7 +510,12 @@ ur_result_t urEnqueueUSMMemcpy(ur_queue_handle_t hQueue, bool blocking,
}
// We are going to release this event in our callback so we need to
// retain if the user wants a copy.
CL_RETURN_ON_FAILURE(clRetainEvent(FinalCopyEvent));
cl_int RetainResult = clRetainEvent(FinalCopyEvent);
if (RetainResult != CL_SUCCESS) {
ur::opencl::urEventRelease(*phEvent);
*phEvent = nullptr;
CL_RETURN_ON_FAILURE(RetainResult);
}
}

// This self destructs taking the event and allocation with it.
Expand All @@ -521,6 +530,10 @@ ur_result_t urEnqueueUSMMemcpy(ur_queue_handle_t hQueue, bool blocking,
if (CLErr != CL_SUCCESS) {
// We can attempt to recover gracefully by attempting to wait for the
// copy to finish and deleting the info struct here.
if (phEvent) {
ur::opencl::urEventRelease(*phEvent);
*phEvent = nullptr;
}
clWaitForEvents(1, &HostCopyEvent);
delete DeleterInfo;
clReleaseEvent(HostCopyEvent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ ur_result_t SafeAllocate(ur_context_handle_t Context, ur_device_handle_t Device,
uptr Size, const ur_usm_desc_t *Properties,
ur_usm_pool_handle_t Pool, AllocType Type,
void **Allocated) {
if (!Device &&
(Type == AllocType::DEVICE_USM || Type == AllocType::MEM_BUFFER ||
Type == AllocType::SHARED_USM)) {
return UR_RESULT_ERROR_INVALID_NULL_HANDLE;
}
DeviceType DevieType =
Device ? GetDeviceType(Context, Device) : DeviceType::UNKNOWN;
switch (Type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,17 @@ ur_result_t DeviceInfo::allocShadowMemory() {
&ShadowContext));
Shadow = GetShadowMemory(ShadowContext, Handle, Type);
assert(Shadow && "Failed to get shadow memory");
UR_CALL(Shadow->Setup());
auto Result = Shadow->Setup();
// Release the shadow context if the operation failed or if the device type is
// CPU. Note that the singleton CPU shadow does not store per-device contexts.
if (Result != UR_RESULT_SUCCESS || Type == DeviceType::CPU) {
[[maybe_unused]] auto ReleaseResult =
getContext()->urDdiTable.Context.pfnRelease(ShadowContext);
assert(ReleaseResult == UR_RESULT_SUCCESS);
}
Comment on lines +100 to +104

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did this just leak on the cpu type before?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, CPU shadow context Destroy() function does not release this context. For GPU type it is done in ShadowMemoryGPU::Destroy()

if (Result != UR_RESULT_SUCCESS) {
return Result;
}
UR_LOG_L(getContext()->logger, INFO, "ShadowMemory(Global): {} - {}",
(void *)Shadow->ShadowBegin, (void *)Shadow->ShadowEnd);
return UR_RESULT_SUCCESS;
Expand Down
Loading