Skip to content

Commit 41ec74c

Browse files
[SYCL][Docs] Make last event optional for queues with no previous work (#16645)
This commit changes the extension documentation and implementation of ext_oneapi_get_last_event to return an std::optional and return a std::nullopt for the case where the queue had no work previously submitted to it. --------- Signed-off-by: Larsen, Steffen <[email protected]>
1 parent a3c95ff commit 41ec74c

File tree

11 files changed

+73
-36
lines changed

11 files changed

+73
-36
lines changed

sycl/doc/extensions/experimental/sycl_ext_oneapi_in_order_queue_events.asciidoc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ namespace sycl {
9696
class queue {
9797
...
9898
99-
event ext_oneapi_get_last_event() const { /*...*/ }
99+
std::optional<event> ext_oneapi_get_last_event() const { /*...*/ }
100100
101101
void ext_oneapi_set_external_event(const event &external_event) { /*...*/ }
102102
}
@@ -113,13 +113,16 @@ These new APIs have the following behaviour:
113113
a|
114114
[source, c++]
115115
----
116-
event ext_oneapi_get_last_event() const;
116+
std::optional<event> ext_oneapi_get_last_event() const;
117117
----
118118
| Returns an event representing the execution of the last command submitted to
119119
the queue. If a call to `ext_oneapi_set_external_event()` on the queue happened
120120
after all previously submitted commands to the queue, this function returns a
121121
copy of the event that was passed to `ext_oneapi_set_external_event()`.
122122

123+
If no commands have been submitted to the queue prior to a call to
124+
`ext_oneapi_set_external_event()`, the call will return `std::nullopt`.
125+
123126
Calls to this member function throw a `sycl::exception` with `errc::invalid` if
124127
the queue does not have the `property::queue::in_order` property.
125128

sycl/include/sycl/detail/optional.hpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ template <typename T> class optional {
2626
template <typename U>
2727
constexpr optional(const optional<U> &Other)
2828
: ContainsValue{Other.ContainsValue} {
29-
new (Storage) T(Other.Value);
29+
new (Storage) T(Other.value());
3030
}
3131
template <typename U>
32-
constexpr optional(optional<U> &&Other)
33-
: ContainsValue{std::move(Other.ContainsValue)} {
34-
new (Storage) T(std::move(Other.Value));
32+
constexpr optional(optional<U> &&Other) : ContainsValue{Other.ContainsValue} {
33+
new (Storage) T(std::move(Other.value()));
34+
Other.ContainsValue = false;
3535
}
3636

3737
constexpr optional(T &&Value) : ContainsValue{true} {
@@ -137,6 +137,11 @@ template <typename T> class optional {
137137
constexpr T &&operator*() && { return value(); }
138138
constexpr const T &&operator*() const && { return value(); }
139139

140+
constexpr operator std::optional<T>() {
141+
return has_value() ? std::optional<T>{value()}
142+
: std::optional<T>{std::nullopt};
143+
}
144+
140145
private:
141146
alignas(alignof(T)) char Storage[sizeof(T)] = {0};
142147
bool ContainsValue = false;

sycl/include/sycl/queue.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2738,7 +2738,9 @@ class __SYCL_EXPORT queue : public detail::OwnerLessBase<queue> {
27382738

27392739
ur_native_handle_t getNative(int32_t &NativeHandleDesc) const;
27402740

2741-
event ext_oneapi_get_last_event() const;
2741+
std::optional<event> ext_oneapi_get_last_event() const {
2742+
return static_cast<std::optional<event>>(ext_oneapi_get_last_event_impl());
2743+
}
27422744

27432745
void ext_oneapi_set_external_event(const event &external_event);
27442746

@@ -3007,6 +3009,11 @@ class __SYCL_EXPORT queue : public detail::OwnerLessBase<queue> {
30073009
const std::vector<event> &DepEvents);
30083010
const property_list &getPropList() const;
30093011

3012+
// Helper implementation for ext_oneapi_get_last_event. This is needed to
3013+
// avoid issues where std::optional has a different layout between user-code
3014+
// and library.
3015+
sycl::detail::optional<event> ext_oneapi_get_last_event_impl() const;
3016+
30103017
template <typename KernelName>
30113018
static constexpr detail::code_location getCodeLocation() {
30123019
return {detail::getKernelFileName<KernelName>(),

sycl/source/detail/queue_impl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ event queue_impl::memcpyFromDeviceGlobal(
277277
DeviceGlobalPtr, IsDeviceImageScope, Self, NumBytes, Offset, Dest);
278278
}
279279

280-
event queue_impl::getLastEvent() {
280+
sycl::detail::optional<event> queue_impl::getLastEvent() {
281281
{
282282
// The external event is required to finish last if set, so it is considered
283283
// the last event if present.
@@ -287,12 +287,12 @@ event queue_impl::getLastEvent() {
287287
}
288288

289289
std::lock_guard<std::mutex> Lock{MMutex};
290+
if (MGraph.expired() && !MDefaultGraphDeps.LastEventPtr)
291+
return std::nullopt;
290292
if (MDiscardEvents)
291293
return createDiscardedEvent();
292294
if (!MGraph.expired() && MExtGraphDeps.LastEventPtr)
293295
return detail::createSyclObjFromImpl<event>(MExtGraphDeps.LastEventPtr);
294-
if (!MDefaultGraphDeps.LastEventPtr)
295-
MDefaultGraphDeps.LastEventPtr = std::make_shared<event_impl>(std::nullopt);
296296
return detail::createSyclObjFromImpl<event>(MDefaultGraphDeps.LastEventPtr);
297297
}
298298

sycl/source/detail/queue_impl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class queue_impl {
180180
#endif
181181
}
182182

183-
event getLastEvent();
183+
sycl::detail::optional<event> getLastEvent();
184184

185185
private:
186186
void queue_impl_interop(ur_queue_handle_t UrQueue) {

sycl/source/queue.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,13 @@ getBarrierEventForInorderQueueHelper(const detail::QueueImplPtr QueueImpl) {
288288
assert(!QueueImpl->getCommandGraph() &&
289289
"Should not be called in on graph recording.");
290290

291-
return QueueImpl->getLastEvent();
291+
sycl::detail::optional<event> LastEvent = QueueImpl->getLastEvent();
292+
if (LastEvent)
293+
return *LastEvent;
294+
295+
// If there was no last event, we create an empty one.
296+
return detail::createSyclObjFromImpl<event>(
297+
std::make_shared<detail::event_impl>(std::nullopt));
292298
}
293299

294300
/// Prevents any commands submitted afterward to this queue from executing
@@ -406,16 +412,22 @@ bool queue::device_has(aspect Aspect) const {
406412
// TODO(#15184) Remove this function in the next ABI-breaking window.
407413
bool queue::ext_codeplay_supports_fusion() const { return false; }
408414

409-
event queue::ext_oneapi_get_last_event() const {
415+
sycl::detail::optional<event> queue::ext_oneapi_get_last_event_impl() const {
410416
if (!is_in_order())
411417
throw sycl::exception(
412418
make_error_code(errc::invalid),
413419
"ext_oneapi_get_last_event() can only be called on in-order queues.");
414420

415-
event LastEvent = impl->getLastEvent();
421+
sycl::detail::optional<event> LastEvent = impl->getLastEvent();
422+
423+
// If there was no last event, the queue is yet to have any work submitted and
424+
// we return a std::nullopt.
425+
if (!LastEvent)
426+
return std::nullopt;
427+
416428
// If the last event was discarded or a NOP, we insert a marker to represent
417429
// an event at end.
418-
auto LastEventImpl = detail::getSyclObjImpl(LastEvent);
430+
auto LastEventImpl = detail::getSyclObjImpl(*LastEvent);
419431
if (LastEventImpl->isDiscarded() || LastEventImpl->isNOP())
420432
LastEvent =
421433
detail::createSyclObjFromImpl<event>(impl->insertMarkerEvent(impl));

sycl/test-e2e/InOrderEventsExt/get_last_event.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,18 @@
2121

2222
template <typename F>
2323
int Check(const sycl::queue &Q, const char *CheckName, const F &CheckFunc) {
24-
sycl::event E = CheckFunc();
25-
if (E != Q.ext_oneapi_get_last_event()) {
24+
std::optional<sycl::event> E = CheckFunc();
25+
if (!E) {
26+
std::cout << "No result event return by CheckFunc()" << std::endl;
27+
return 1;
28+
}
29+
std::optional<sycl::event> LastEvent = Q.ext_oneapi_get_last_event();
30+
if (!LastEvent) {
31+
std::cout << "No result event return by ext_oneapi_get_last_event()"
32+
<< std::endl;
33+
return 1;
34+
}
35+
if (*E != *LastEvent) {
2636
std::cout << "Failed " << CheckName << std::endl;
2737
return 1;
2838
}
@@ -34,12 +44,17 @@ int main() {
3444

3545
int Failed = 0;
3646

37-
// Check that a valid event is returned on the empty queue.
38-
Q.ext_oneapi_get_last_event().wait();
47+
// Check that a std::nullopt is returned on the empty queue.
48+
std::optional<sycl::event> EmptyEvent = Q.ext_oneapi_get_last_event();
49+
if (EmptyEvent.has_value()) {
50+
std::cout << "Unexpected event return by ext_oneapi_get_last_event()"
51+
<< std::endl;
52+
++Failed;
53+
}
3954

4055
// Check that a valid event is returned after enqueuing work without events.
4156
sycl::ext::oneapi::experimental::single_task(Q, []() {});
42-
Q.ext_oneapi_get_last_event().wait();
57+
Q.ext_oneapi_get_last_event()->wait();
4358

4459
// Check event equivalences - This is an implementation detail, but useful
4560
// for checking behavior.

sycl/test/abi/sycl_symbols_linux.dump

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3660,8 +3660,8 @@ _ZNK4sycl3_V15queue16get_backend_infoINS0_4info6device7versionEEENS0_6detail20is
36603660
_ZNK4sycl3_V15queue16get_backend_infoINS0_4info8platform7versionEEENS0_6detail20is_backend_info_descIT_E11return_typeEv
36613661
_ZNK4sycl3_V15queue20ext_oneapi_get_graphEv
36623662
_ZNK4sycl3_V15queue20ext_oneapi_get_stateEv
3663-
_ZNK4sycl3_V15queue25ext_oneapi_get_last_eventEv
36643663
_ZNK4sycl3_V15queue28ext_codeplay_supports_fusionEv
3664+
_ZNK4sycl3_V15queue30ext_oneapi_get_last_event_implEv
36653665
_ZNK4sycl3_V15queue3getEv
36663666
_ZNK4sycl3_V15queue8get_infoINS0_4info5queue15reference_countEEENS0_6detail18is_queue_info_descIT_E11return_typeEv
36673667
_ZNK4sycl3_V15queue8get_infoINS0_4info5queue6deviceEEENS0_6detail18is_queue_info_descIT_E11return_typeEv

sycl/test/abi/sycl_symbols_windows.dump

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3847,7 +3847,8 @@
38473847
?ext_oneapi_get_graph@queue@_V1@sycl@@QEBA?AV?$command_graph@$0A@@experimental@oneapi@ext@23@XZ
38483848
?ext_oneapi_get_kernel@kernel_bundle_plain@detail@_V1@sycl@@AEAA?AVkernel@34@Vstring_view@234@@Z
38493849
?ext_oneapi_get_kernel@kernel_bundle_plain@detail@_V1@sycl@@QEAA?AVkernel@34@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z
3850-
?ext_oneapi_get_last_event@queue@_V1@sycl@@QEBA?AVevent@23@XZ
3850+
?ext_oneapi_get_last_event_impl@queue@_V1@sycl@@AEBA?AV?$optional@Vevent@_V1@sycl@@@detail@23@XZ
3851+
?ext_oneapi_get_last_event@queue@_V1@sycl@@QEBA?AV?$optional@Vevent@_V1@sycl@@@std@@XZ
38513852
?ext_oneapi_get_state@queue@_V1@sycl@@QEBA?AW4queue_state@experimental@oneapi@ext@23@XZ
38523853
?ext_oneapi_graph@handler@_V1@sycl@@QEAAXV?$command_graph@$00@experimental@oneapi@ext@23@@Z
38533854
?ext_oneapi_graph@queue@_V1@sycl@@QEAA?AVevent@23@V?$command_graph@$00@experimental@oneapi@ext@23@AEBUcode_location@detail@23@@Z

sycl/unittests/Extensions/EnqueueFunctionsEvents.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ class EnqueueFunctionsEventsTests : public ::testing::Test {
111111

112112
inline void CheckLastEventDiscarded(sycl::queue &Q) {
113113
auto QueueImplPtr = sycl::detail::getSyclObjImpl(Q);
114-
event LastEvent = QueueImplPtr->getLastEvent();
115-
auto LastEventImplPtr = sycl::detail::getSyclObjImpl(LastEvent);
114+
sycl::detail::optional<event> LastEvent = QueueImplPtr->getLastEvent();
115+
ASSERT_TRUE(LastEvent.has_value());
116+
auto LastEventImplPtr = sycl::detail::getSyclObjImpl(*LastEvent);
116117
ASSERT_TRUE(LastEventImplPtr->isDiscarded());
117118
}
118119

sycl/unittests/Extensions/GetLastEvent.cpp

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,9 @@ TEST(GetLastEventEmptyQueue, CheckEmptyQueueLastEvent) {
2929
unittest::UrMock<> Mock;
3030
platform Plt = sycl::platform();
3131

32-
MarkerEventLatest = nullptr;
33-
mock::getCallbacks().set_after_callback("urEnqueueEventsWait",
34-
&redefinedEnqueueEventsWaitAfter);
35-
mock::getCallbacks().set_before_callback("urEventRelease",
36-
&redefinedEventRelease);
37-
3832
queue Q{property::queue::in_order{}};
39-
event E = Q.ext_oneapi_get_last_event();
40-
ur_event_handle_t UREvent = detail::getSyclObjImpl(E)->getHandle();
41-
ASSERT_NE(MarkerEventLatest, ur_event_handle_t{nullptr});
42-
ASSERT_EQ(UREvent, MarkerEventLatest);
33+
std::optional<event> E = Q.ext_oneapi_get_last_event();
34+
ASSERT_FALSE(E.has_value());
4335
}
4436

4537
TEST(GetLastEventEmptyQueue, CheckEventlessWorkQueue) {
@@ -57,8 +49,9 @@ TEST(GetLastEventEmptyQueue, CheckEventlessWorkQueue) {
5749
// The following single_task does not return an event, so it is expected that
5850
// the last event query creates a new marker event.
5951
sycl::ext::oneapi::experimental::single_task<TestKernel<>>(Q, []() {});
60-
event E = Q.ext_oneapi_get_last_event();
61-
ur_event_handle_t UREvent = detail::getSyclObjImpl(E)->getHandle();
52+
std::optional<event> E = Q.ext_oneapi_get_last_event();
53+
ASSERT_TRUE(E.has_value());
54+
ur_event_handle_t UREvent = detail::getSyclObjImpl(*E)->getHandle();
6255
ASSERT_NE(MarkerEventLatest, ur_event_handle_t{nullptr});
6356
ASSERT_EQ(UREvent, MarkerEventLatest);
6457
}

0 commit comments

Comments
 (0)