intel / intel/llvm

[SYCL][Graph] Deadlock when using ext_oneapi_set_external_event to transition queue into recording mode

Open
#20,563 1 comment 0 reactions 0 assignees View on GitHub
bug sycl-graph
Dominant language
LLVM
Stars
1.5k
Forks
854
Avg merge
3d 17h
Merged PRs (30d)
137

Description

Initially reported by @slawekptak

### Describe the bug

Setting external events onto an in-order queue from a queue that is a recording to a queue that is not recording to cause a transition into recording mode results in a deadlock. This is caused by the queue lock attempted to be acquired twice the implementation. It should be fixed to enable graph compatibility with this extension.

### To reproduce

The below code snippet can be used to reproduce the issue and will hang during the submission to Q2.
```cpp
// Compilation clang++ -fsycl transitive_set_external_event.cpp -o transitive_set_external_event
#include

#include
#include
#include
#include

using namespace sycl;
namespace exp_ext = ext::oneapi::experimental;

int main() {
constexpr size_t Size = 128;

device Dev = device::get_devices()[0];
context Ctx{Dev};

queue Q1{Ctx, Dev, {property::queue::in_order{}}};
queue Q2{Ctx, Dev, {property::queue::in_order{}}};

std::vector HostA(Size), HostB(Size);
std::iota(HostA.begin(), HostA.end(), 1);
std::iota(HostB.begin(), HostB.end(), 100);

int *A = malloc_device(Size, Q1);
int *B = malloc_device(Size, Q1);

Q1.copy(HostA.data(), A, Size);
Q1.copy(HostB.data(), B, Size);
Q1.wait_and_throw();

exp_ext::command_graph Graph{Ctx, Dev};

// Begin recording on Q1
Graph.begin_recording(Q1);

// Submit a small kernel on Q1 that increments A
auto E1 = Q1.submit([&](handler &h) {
h.parallel_for(range<1>{Size}, [=](id<1> i) { A[i] += 1; });
});

// Set external event to depend on E1 for Q2.
Q2.ext_oneapi_set_external_event(E1);

// Submissions to Q2 should be considered part of the same graph due to
// the external event linking into recording mode. Submit a kernel on Q2
// that multiplies B by 2 and depends implicitly on the external event.
auto E2 = Q2.submit([&](handler &h) {
h.parallel_for(range<1>{Size}, [=](id<1> i) { B[i] *= 2; });
});

Graph.end_recording();

// Finalize the graph into an executable
auto Exec = Graph.finalize();

Q1.ext_oneapi_graph(Exec);
Q1.wait_and_throw();

// Copy results back to host for verification
std::vector OutA(Size), OutB(Size);
Q1.copy(A, OutA.data(), Size);
Q1.copy(B, OutB.data(), Size);
Q1.wait_and_throw();

// Verify expected result computed on host
for (size_t i = 0; i < Size; ++i) {
int expectedA = HostA[i] + 1;
int expectedB = HostB[i] * 2;
if (OutA[i] != expectedA || OutB[i] != expectedB) {
std::cerr << "Mismatch at " << i << ": got (" << OutA[i] << ", "
<< OutB[i] << ") expected (" << expectedA
<< ", " << expectedB << ")\n";
return 1;
}
}

// Cleanup
free(A, Q1);
free(B, Q1);

std::cout << "PASS\n";
return 0;
}

```

### Environment

- DPC++ version: produced with 29435fc9e1be
- Other environment details not relevant for producing deadlock

### Additional context

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.