Skip to content

[SYCL][PI][L0] Fix for race condition on Event's ZeCommandList member. #2652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 35 additions & 38 deletions sycl/plugins/level_zero/pi_level_zero.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3283,6 +3283,38 @@ pi_result piEventGetProfilingInfo(pi_event Event, pi_profiling_info ParamName,
return PI_SUCCESS;
}

// Recycle the command list associated with this event.
static void recycleEventCommandList(pi_event Event) {
// The implementation of this is slightly tricky. The same event
// can be referred to by multiple threads, so it is possible to
// have a race condition between the read of ZeCommandList and
// it being reset to nullptr in another thread.
// But, since the ZeCommandList is uniquely associated with the queue
// for the event, we use the locking that we already have to do on the
// queue to also serve as the thread safety mechanism for the
// Event's ZeCommandList.
auto Queue = Event->Queue;

// Lock automatically releases when this goes out of scope.
std::lock_guard<std::mutex> lock(Queue->PiQueueMutex);

auto EventCommandList = Event->ZeCommandList;

if (EventCommandList) {
// Event has been signaled: If the fence for the associated command list
// is signalled, then reset the fence and command list and add them to the
// available list for reuse in PI calls.
if (Queue->RefCount > 0) {
ze_result_t ZeResult = ZE_CALL_NOCHECK(
zeFenceQueryStatus(Queue->ZeCommandListFenceMap[EventCommandList]));
if (ZeResult == ZE_RESULT_SUCCESS) {
Queue->resetCommandListFenceEntry(EventCommandList, true);
}
}
Event->ZeCommandList = nullptr;
}
}

pi_result piEventsWait(pi_uint32 NumEvents, const pi_event *EventList) {

if (NumEvents && !EventList) {
Expand All @@ -3309,26 +3341,7 @@ pi_result piEventsWait(pi_uint32 NumEvents, const pi_event *EventList) {

// NOTE: we are destroying associated command lists here to free
// resources sooner in case RT is not calling piEventRelease soon enough.
if (EventList[I]->ZeCommandList) {
// Event has been signaled: If the fence for the associated command list
// is signalled, then reset the fence and command list and add them to the
// available list for reuse in PI calls.
auto Queue = EventList[I]->Queue;

// Lock automatically releases when this goes out of scope.
std::lock_guard<std::mutex> lock(Queue->PiQueueMutex);

if (Queue->RefCount > 0) {
ze_result_t ZeResult = ZE_CALL_NOCHECK(zeFenceQueryStatus(
EventList[I]
->Queue->ZeCommandListFenceMap[EventList[I]->ZeCommandList]));
if (ZeResult == ZE_RESULT_SUCCESS) {
EventList[I]->Queue->resetCommandListFenceEntry(
EventList[I]->ZeCommandList, true);
EventList[I]->ZeCommandList = nullptr;
}
}
}
recycleEventCommandList(EventList[I]);
}
return PI_SUCCESS;
}
Expand All @@ -3355,24 +3368,8 @@ pi_result piEventRetain(pi_event Event) {
pi_result piEventRelease(pi_event Event) {
assert(Event);
if (--(Event->RefCount) == 0) {
if (Event->ZeCommandList) {
// If the fence associated with this command list has signalled, then
// Reset the Command List Used in this event and put it back on the
// available list.
auto Queue = Event->Queue;

// Lock automatically releases when this goes out of scope.
std::lock_guard<std::mutex> lock(Queue->PiQueueMutex);

if (Queue->RefCount > 0) {
ze_result_t ZeResult = ZE_CALL_NOCHECK(zeFenceQueryStatus(
Event->Queue->ZeCommandListFenceMap[Event->ZeCommandList]));
if (ZeResult == ZE_RESULT_SUCCESS) {
Event->Queue->resetCommandListFenceEntry(Event->ZeCommandList, true);
}
}
Event->ZeCommandList = nullptr;
}
recycleEventCommandList(Event);

if (Event->CommandType == PI_COMMAND_TYPE_MEM_BUFFER_UNMAP &&
Event->CommandData) {
// Free the memory allocated in the piEnqueueMemBufferMap.
Expand Down