diff --git a/sycl/include/sycl/accessor.hpp b/sycl/include/sycl/accessor.hpp index d862216c7e711..e1fd265aa5d92 100644 --- a/sycl/include/sycl/accessor.hpp +++ b/sycl/include/sycl/accessor.hpp @@ -535,7 +535,7 @@ class __SYCL_EXPORT LocalAccessorBaseHost { protected: template - friend decltype(Obj::impl) getSyclObjImpl(const Obj &SyclObject); + friend decltype(Obj::impl) detail::getSyclObjImpl(const Obj &SyclObject); template friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj); @@ -1209,6 +1209,9 @@ class __SYCL_EBO __SYCL_SPECIAL_CLASS __SYCL_TYPE(accessor) accessor : friend class sycl::stream; friend class sycl::ext::intel::esimd::detail::AccessorPrivateProxy; + template + friend decltype(Obj::impl) detail::getSyclObjImpl(const Obj &SyclObject); + template friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj); @@ -2528,6 +2531,9 @@ class __SYCL_SPECIAL_CLASS local_accessor_base : return Result; } + template + friend decltype(Obj::impl) detail::getSyclObjImpl(const Obj &SyclObject); + template friend T detail::createSyclObjFromImpl(decltype(T::impl) ImplObj); diff --git a/sycl/include/sycl/detail/owner_less_base.hpp b/sycl/include/sycl/detail/owner_less_base.hpp index 026fe10c04d42..d082afd954d1c 100644 --- a/sycl/include/sycl/detail/owner_less_base.hpp +++ b/sycl/include/sycl/detail/owner_less_base.hpp @@ -42,6 +42,13 @@ template class OwnerLessBase { return getSyclObjImpl(*static_cast(this)) .owner_before(getSyclObjImpl(Other)); } +#else + // On device calls to these functions are disallowed, so declare them but + // don't define them to avoid compilation failures. + bool ext_oneapi_owner_before( + const ext::oneapi::detail::weak_object_base &Other) + const noexcept; + bool ext_oneapi_owner_before(const SyclObjT &Other) const noexcept; #endif }; diff --git a/sycl/include/sycl/ext/oneapi/weak_object.hpp b/sycl/include/sycl/ext/oneapi/weak_object.hpp index 33d797e77f57a..7d74486ac6d46 100644 --- a/sycl/include/sycl/ext/oneapi/weak_object.hpp +++ b/sycl/include/sycl/ext/oneapi/weak_object.hpp @@ -50,12 +50,13 @@ class weak_object : public detail::weak_object_base { weak_object &operator=(const SYCLObjT &SYCLObj) noexcept { // Create weak_ptr from the shared_ptr to SYCLObj's implementation object. - this->MObjWeakPtr = sycl::detail::getSyclObjImpl(SYCLObj); + this->MObjWeakPtr = GetWeakImpl(SYCLObj); return *this; } weak_object &operator=(const weak_object &Other) noexcept = default; weak_object &operator=(weak_object &&Other) noexcept = default; +#ifndef __SYCL_DEVICE_ONLY__ std::optional try_lock() const noexcept { auto MObjImplPtr = this->MObjWeakPtr.lock(); if (!MObjImplPtr) @@ -69,6 +70,12 @@ class weak_object : public detail::weak_object_base { "Referenced object has expired."); return *OptionalObj; } +#else + // On device calls to these functions are disallowed, so declare them but + // don't define them to avoid compilation failures. + std::optional try_lock() const noexcept; + SYCLObjT lock() const; +#endif // __SYCL_DEVICE_ONLY__ }; // Specialization of weak_object for buffer as it needs additional members @@ -96,7 +103,7 @@ class weak_object> weak_object &operator=(const buffer_type &SYCLObj) noexcept { // Create weak_ptr from the shared_ptr to SYCLObj's implementation object. - this->MObjWeakPtr = sycl::detail::getSyclObjImpl(SYCLObj); + this->MObjWeakPtr = GetWeakImpl(SYCLObj); this->MRange = SYCLObj.Range; this->MOffsetInBytes = SYCLObj.OffsetInBytes; this->MIsSubBuffer = SYCLObj.IsSubBuffer; @@ -105,6 +112,7 @@ class weak_object> weak_object &operator=(const weak_object &Other) noexcept = default; weak_object &operator=(weak_object &&Other) noexcept = default; +#ifndef __SYCL_DEVICE_ONLY__ std::optional try_lock() const noexcept { auto MObjImplPtr = this->MObjWeakPtr.lock(); if (!MObjImplPtr) @@ -119,6 +127,12 @@ class weak_object> "Referenced object has expired."); return *OptionalObj; } +#else + // On device calls to these functions are disallowed, so declare them but + // don't define them to avoid compilation failures. + std::optional try_lock() const noexcept; + buffer_type lock() const; +#endif // __SYCL_DEVICE_ONLY__ private: // Additional members required for recreating buffers. diff --git a/sycl/include/sycl/ext/oneapi/weak_object_base.hpp b/sycl/include/sycl/ext/oneapi/weak_object_base.hpp index 7dc10e7e86e1a..71efcf2fe23b1 100644 --- a/sycl/include/sycl/ext/oneapi/weak_object_base.hpp +++ b/sycl/include/sycl/ext/oneapi/weak_object_base.hpp @@ -29,7 +29,7 @@ template class weak_object_base { constexpr weak_object_base() noexcept : MObjWeakPtr() {} weak_object_base(const SYCLObjT &SYCLObj) noexcept - : MObjWeakPtr(sycl::detail::getSyclObjImpl(SYCLObj)) {} + : MObjWeakPtr(GetWeakImpl(SYCLObj)) {} weak_object_base(const weak_object_base &Other) noexcept = default; weak_object_base(weak_object_base &&Other) noexcept = default; @@ -43,19 +43,36 @@ template class weak_object_base { bool expired() const noexcept { return MObjWeakPtr.expired(); } +#ifndef __SYCL_DEVICE_ONLY__ bool owner_before(const SYCLObjT &Other) const noexcept { - return MObjWeakPtr.owner_before(sycl::detail::getSyclObjImpl(Other)); + return MObjWeakPtr.owner_before(GetWeakImpl(Other)); } bool owner_before(const weak_object_base &Other) const noexcept { return MObjWeakPtr.owner_before(Other.MObjWeakPtr); } +#else + // On device calls to these functions are disallowed, so declare them but + // don't define them to avoid compilation failures. + bool owner_before(const SYCLObjT &Other) const noexcept; + bool owner_before(const weak_object_base &Other) const noexcept; +#endif // __SYCL_DEVICE_ONLY__ protected: +#ifndef __SYCL_DEVICE_ONLY__ // Store a weak variant of the impl in the SYCLObjT. typename std::invoke_result_t< decltype(sycl::detail::getSyclObjImpl), SYCLObjT>::weak_type MObjWeakPtr; + static decltype(MObjWeakPtr) GetWeakImpl(const SYCLObjT &SYCLObj) { + return sycl::detail::getSyclObjImpl(SYCLObj); + } +#else + // On device we may not have an impl, so we pad with an unused void pointer. + std::weak_ptr MObjWeakPtr; + static std::weak_ptr GetWeakImpl(const SYCLObjT &) { return {}; } +#endif // __SYCL_DEVICE_ONLY__ + template friend decltype(weak_object_base::MObjWeakPtr) detail::getSyclWeakObjImpl(const weak_object_base &WeakObj); diff --git a/sycl/test-e2e/WeakObject/weak_object_copy.cpp b/sycl/test-e2e/WeakObject/weak_object_copy.cpp new file mode 100644 index 0000000000000..9acb2f4c7de98 --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_copy.cpp @@ -0,0 +1,28 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of the copy ctor and assignment operator for +// `weak_object`. + +#include "weak_object_utils.hpp" + +template struct WeakObjectCheckCopy { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj{Obj}; + + sycl::ext::oneapi::weak_object WeakObjCopyCtor{WeakObj}; + sycl::ext::oneapi::weak_object WeakObjCopyAssign = WeakObj; + + assert(!WeakObjCopyCtor.expired()); + assert(!WeakObjCopyAssign.expired()); + + assert(WeakObjCopyCtor.lock() == Obj); + assert(WeakObjCopyAssign.lock() == Obj); + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_expired.cpp b/sycl/test-e2e/WeakObject/weak_object_expired.cpp new file mode 100644 index 0000000000000..15b50c8770e26 --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_expired.cpp @@ -0,0 +1,22 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of `expired()` for `weak_object`. + +#include "weak_object_utils.hpp" + +template struct WeakObjectCheckExpired { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj{Obj}; + sycl::ext::oneapi::weak_object NullWeakObj; + + assert(!WeakObj.expired()); + assert(NullWeakObj.expired()); + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_lock.cpp b/sycl/test-e2e/WeakObject/weak_object_lock.cpp new file mode 100644 index 0000000000000..a46ff10c70177 --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_lock.cpp @@ -0,0 +1,30 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of `lock()` for `weak_object`. + +#include "weak_object_utils.hpp" + +template struct WeakObjectCheckLock { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj{Obj}; + sycl::ext::oneapi::weak_object NullWeakObj; + + SyclObjT LObj = WeakObj.lock(); + assert(LObj == Obj); + + try { + SyclObjT LNull = NullWeakObj.lock(); + assert(false && "Locking empty weak object did not throw."); + } catch (sycl::exception &E) { + assert(E.code() == sycl::make_error_code(sycl::errc::invalid) && + "Unexpected thrown error code."); + } + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_move.cpp b/sycl/test-e2e/WeakObject/weak_object_move.cpp new file mode 100644 index 0000000000000..80842f51375e7 --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_move.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of the copy ctor and assignment operator for +// `weak_object`. + +#include "weak_object_utils.hpp" + +template struct WeakObjectCheckMove { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj1{Obj}; + sycl::ext::oneapi::weak_object WeakObj2{Obj}; + + sycl::ext::oneapi::weak_object WeakObjMoveCtor{ + std::move(WeakObj1)}; + sycl::ext::oneapi::weak_object WeakObjMoveAssign = + std::move(WeakObj2); + + assert(!WeakObjMoveCtor.expired()); + assert(!WeakObjMoveAssign.expired()); + + assert(WeakObjMoveCtor.lock() == Obj); + assert(WeakObjMoveAssign.lock() == Obj); + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_owner_before.cpp b/sycl/test-e2e/WeakObject/weak_object_owner_before.cpp new file mode 100644 index 0000000000000..57adfb38e6b3d --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_owner_before.cpp @@ -0,0 +1,52 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of owner_before semantics for `weak_object`. + +#include "weak_object_utils.hpp" + +template struct WeakObjectCheckOwnerBefore { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj{Obj}; + sycl::ext::oneapi::weak_object NullWeakObj; + + assert((WeakObj.owner_before(NullWeakObj) && + !NullWeakObj.owner_before(WeakObj)) || + (NullWeakObj.owner_before(WeakObj) && + !WeakObj.owner_before(NullWeakObj))); + + assert(!WeakObj.owner_before(Obj)); + assert(!Obj.ext_oneapi_owner_before(WeakObj)); + + assert(!Obj.ext_oneapi_owner_before(Obj)); + } +}; + +template struct WeakObjectCheckOwnerBeforeMulti { + void operator()(SyclObjT Obj1, SyclObjT Obj2) { + sycl::ext::oneapi::weak_object WeakObj1{Obj1}; + sycl::ext::oneapi::weak_object WeakObj2{Obj2}; + + assert( + (WeakObj1.owner_before(WeakObj2) && !WeakObj2.owner_before(WeakObj1)) || + (WeakObj2.owner_before(WeakObj1) && !WeakObj1.owner_before(WeakObj2))); + + assert(!WeakObj1.owner_before(Obj1)); + assert(!Obj1.ext_oneapi_owner_before(WeakObj1)); + + assert(!WeakObj2.owner_before(Obj2)); + assert(!Obj2.ext_oneapi_owner_before(WeakObj2)); + + assert((Obj1.ext_oneapi_owner_before(Obj2) && + !Obj2.ext_oneapi_owner_before(Obj1)) || + (Obj2.ext_oneapi_owner_before(Obj1) && + !Obj1.ext_oneapi_owner_before(Obj2))); + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + runTestMulti(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_owner_less.cpp b/sycl/test-e2e/WeakObject/weak_object_owner_less.cpp new file mode 100644 index 0000000000000..6362342dd2826 --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_owner_less.cpp @@ -0,0 +1,96 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of owner_less semantics for `weak_object`. + +#include "weak_object_utils.hpp" + +#include + +template struct WeakObjectCheckOwnerLess { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj{Obj}; + sycl::ext::oneapi::weak_object NullWeakObj; + sycl::ext::oneapi::owner_less Comparator; + + assert((Comparator(WeakObj, NullWeakObj) && + !Comparator(NullWeakObj, WeakObj)) || + (Comparator(NullWeakObj, WeakObj) && + !Comparator(WeakObj, NullWeakObj))); + + assert(!Comparator(WeakObj, Obj)); + assert(!Comparator(Obj, WeakObj)); + } +}; + +template struct WeakObjectCheckOwnerLessMulti { + void operator()(SyclObjT Obj1, SyclObjT Obj2) { + sycl::ext::oneapi::weak_object WeakObj1{Obj1}; + sycl::ext::oneapi::weak_object WeakObj2{Obj2}; + sycl::ext::oneapi::owner_less Comparator; + + assert( + (Comparator(WeakObj1, WeakObj2) && !Comparator(WeakObj2, WeakObj1)) || + (Comparator(WeakObj2, WeakObj1) && !Comparator(WeakObj1, WeakObj2))); + + assert(!Comparator(WeakObj1, Obj1)); + assert(!Comparator(Obj1, WeakObj1)); + + assert(!Comparator(WeakObj2, Obj2)); + assert(!Comparator(Obj2, WeakObj2)); + } +}; + +template struct WeakObjectCheckOwnerLessMap { + void operator()(SyclObjT Obj1, SyclObjT Obj2) { + sycl::ext::oneapi::weak_object WeakObj1{Obj1}; + sycl::ext::oneapi::weak_object WeakObj2{Obj2}; + + std::map, int, + sycl::ext::oneapi::owner_less> + Map; + Map[WeakObj1] = 1; + Map[WeakObj2] = 2; + + assert(Map.size() == (size_t)2); + assert(Map[WeakObj1] == 1); + assert(Map[WeakObj2] == 2); + assert(Map[Obj1] == 1); + assert(Map[Obj2] == 2); + + Map[WeakObj1] = 2; + Map[WeakObj2] = 3; + + assert(Map.size() == (size_t)2); + assert(Map[WeakObj1] == 2); + assert(Map[WeakObj2] == 3); + assert(Map[Obj1] == 2); + assert(Map[Obj2] == 3); + + Map[Obj1] = 5; + Map[Obj2] = 6; + + assert(Map.size() == (size_t)2); + assert(Map[WeakObj1] == 5); + assert(Map[WeakObj2] == 6); + assert(Map[Obj1] == 5); + assert(Map[Obj2] == 6); + + Map[sycl::ext::oneapi::weak_object{Obj1}] = 10; + Map[sycl::ext::oneapi::weak_object{Obj2}] = 13; + + assert(Map.size() == (size_t)2); + assert(Map[WeakObj1] == 10); + assert(Map[WeakObj2] == 13); + assert(Map[Obj1] == 10); + assert(Map[Obj2] == 13); + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + runTestMulti(Q); + runTestMulti(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_reset.cpp b/sycl/test-e2e/WeakObject/weak_object_reset.cpp new file mode 100644 index 0000000000000..7d9e80748aa9b --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_reset.cpp @@ -0,0 +1,35 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of `reset()` for `weak_object`. + +#include "weak_object_utils.hpp" + +template struct WeakObjectCheckReset { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj{Obj}; + sycl::ext::oneapi::weak_object NullWeakObj; + + WeakObj.reset(); + assert(WeakObj.expired()); + assert(!WeakObj.owner_before(NullWeakObj)); + assert(!NullWeakObj.owner_before(WeakObj)); + + std::optional TLObj = WeakObj.try_lock(); + assert(!TLObj.has_value()); + + try { + SyclObjT LObj = WeakObj.lock(); + assert(false && "Locking reset weak object did not throw."); + } catch (sycl::exception &E) { + assert(E.code() == sycl::make_error_code(sycl::errc::invalid) && + "Unexpected thrown error code."); + } + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_try_lock.cpp b/sycl/test-e2e/WeakObject/weak_object_try_lock.cpp new file mode 100644 index 0000000000000..5a621f8ca5d30 --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_try_lock.cpp @@ -0,0 +1,27 @@ +// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out +// RUN: %BE_RUN_PLACEHOLDER %t.out + +// This test checks the behavior of `try_lock()` for `weak_object`. + +#include "weak_object_utils.hpp" + +template struct WeakObjectCheckTryLock { + void operator()(SyclObjT Obj) { + sycl::ext::oneapi::weak_object WeakObj{Obj}; + sycl::ext::oneapi::weak_object NullWeakObj; + + std::optional TLObj = WeakObj.try_lock(); + std::optional TLNull = NullWeakObj.try_lock(); + + assert(TLObj.has_value()); + assert(!TLNull.has_value()); + + assert(TLObj.value() == Obj); + } +}; + +int main() { + sycl::queue Q; + runTest(Q); + return 0; +} diff --git a/sycl/test-e2e/WeakObject/weak_object_utils.hpp b/sycl/test-e2e/WeakObject/weak_object_utils.hpp new file mode 100644 index 0000000000000..64192b3624e92 --- /dev/null +++ b/sycl/test-e2e/WeakObject/weak_object_utils.hpp @@ -0,0 +1,160 @@ +// Utilities for weak_object testing + +#include + +class TestKernel1; +class TestKernel2; + +void MaterializeTestKernels(sycl::queue Q) { + if (false) { + Q.single_task([]() {}); + Q.single_task([]() {}); + } +} + +template