From 1505b3e83d2810fbff92b645a1b67e18918900c0 Mon Sep 17 00:00:00 2001 From: Kseniya Tikhomirova Date: Fri, 31 Jul 2026 16:46:12 +0200 Subject: [PATCH] [libsycl] Add liboffload context handler to default context (#213249) Signed-off-by: Tikhomirova, Kseniya --- libsycl/src/detail/context_impl.cpp | 40 ++++++++++++++++++++++----- libsycl/src/detail/context_impl.hpp | 41 ++++++++++++++++++++-------- libsycl/src/detail/platform_impl.cpp | 9 +++++- libsycl/src/detail/queue_impl.cpp | 3 ++ libsycl/unittests/mock/helpers.cpp | 27 ++++++++++++++++++ libsycl/unittests/mock/helpers.hpp | 4 +++ libsycl/unittests/mock/mock.cpp | 10 +++++++ 7 files changed, 115 insertions(+), 19 deletions(-) diff --git a/libsycl/src/detail/context_impl.cpp b/libsycl/src/detail/context_impl.cpp index 264716fbdfad..66a63131b49b 100644 --- a/libsycl/src/detail/context_impl.cpp +++ b/libsycl/src/detail/context_impl.cpp @@ -13,15 +13,41 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL namespace detail { -void ContextImpl::iterateDevices( - const std::function &callback) const { - // TODO: Intentionally don't store devices in context now. This class should - // be reimplemented once liboffload adds context support. Treat context as - // default context that is associated with all devices in the platform. - return MPlatform.iterateDevices(info::device_type::all, callback); +ContextImpl::ContextImpl(std::vector &&DeviceList, + const async_handler &AsyncHandler, + const property_list &PropList, Private) + : MAsyncHandler(AsyncHandler), MDevices(DeviceList) { + (void)PropList; + + assert(!MDevices.empty() && "Device list must not be empty"); + + std::vector DeviceIds; + DeviceIds.reserve(MDevices.size()); + for (DeviceImpl *D : MDevices) { + assert(D && "Device list must not contain null entries"); + DeviceIds.push_back(D->getOLHandle()); + } + + callAndThrow(olCreateContext, DeviceIds.size(), DeviceIds.data(), + &MOffloadContext); } -backend ContextImpl::getBackend() const { return MPlatform.getBackend(); } +ContextImpl::~ContextImpl() { + assert(MOffloadContext && "Context must be created in ctor"); + std::ignore = olDestroyContext(MOffloadContext); +} + +PlatformImpl &ContextImpl::getPlatformImpl() const { + return MDevices[0]->getPlatformImpl(); +} + +void ContextImpl::iterateDevices( + const std::function &callback) const { + for (DeviceImpl *Device : MDevices) + callback(Device); +} + +backend ContextImpl::getBackend() const { return MDevices[0]->getBackend(); } } // namespace detail _LIBSYCL_END_NAMESPACE_SYCL diff --git a/libsycl/src/detail/context_impl.hpp b/libsycl/src/detail/context_impl.hpp index 4dcbda6c95f8..ec9c8d10aca7 100644 --- a/libsycl/src/detail/context_impl.hpp +++ b/libsycl/src/detail/context_impl.hpp @@ -15,6 +15,7 @@ #ifndef _LIBSYCL_CONTEXT_IMPL #define _LIBSYCL_CONTEXT_IMPL +#include #include #include @@ -23,16 +24,14 @@ #include _LIBSYCL_BEGIN_NAMESPACE_SYCL + +class property_list; + namespace detail { class PlatformImpl; class DeviceImpl; -// TODO: Presence of context object is essential for many APIs. Current -// implementation of this class is a way to support them in case of absence of -// context support in liboffload. For backends where context exists and -// participates in operations, liboffload plugins create and use default context -// that represents all devices in that platform. Duplicating this logic here. /// Context represents the runtime data structures and state required by a SYCL /// backend API to interact with a group of devices associated with a platform. class ContextImpl : public std::enable_shared_from_this { @@ -41,12 +40,22 @@ class ContextImpl : public std::enable_shared_from_this { }; public: - /// Constructs a ContextImpl using a platform. + /// Constructs a context implementation for the provided devices. /// - /// Newly created instance represents all devices in platform. + /// \param DeviceList is the list of devices associated with this context. + /// \param AsyncHandler is a SYCL asynchronous exception handler. + /// \param PropList is a list of context properties. + ContextImpl(std::vector &&DeviceList, + const async_handler &AsyncHandler, const property_list &PropList, + Private); + + /// Releases the underlying offload context handle. + ~ContextImpl(); + + /// Gets asynchronous exception handler. /// - /// \param Platform is a platform to associate this context with. - ContextImpl(PlatformImpl &Platform, Private) : MPlatform(Platform) {} + /// \return an instance of SYCL async_handler. + const async_handler &get_async_handler() const { return MAsyncHandler; } /// Constructs a ContextImpl with a provided arguments. Variadic helper. /// Restrics ways of ContextImpl creation. @@ -55,8 +64,16 @@ public: return std::make_shared(std::forward(args)..., Private{}); } + /// Returns the raw underlying offload context handle. + /// + /// The caller is responsible for ensuring that the returned handle is only + /// used while this ContextImpl object is alive. + /// + /// \return the raw offload context handle. + const ol_context_handle_t &getOLHandleRef() const { return MOffloadContext; } + /// \return the platform this context is associated with. - PlatformImpl &getPlatformImpl() const { return MPlatform; } + PlatformImpl &getPlatformImpl() const; /// Calls "callback" with every device associated /// with this context. @@ -66,7 +83,9 @@ public: backend getBackend() const; private: - PlatformImpl &MPlatform; + const async_handler MAsyncHandler; + const std::vector MDevices; + ol_context_handle_t MOffloadContext{}; }; } // namespace detail diff --git a/libsycl/src/detail/platform_impl.cpp b/libsycl/src/detail/platform_impl.cpp index 96d52d1363a9..1d81b848a21d 100644 --- a/libsycl/src/detail/platform_impl.cpp +++ b/libsycl/src/detail/platform_impl.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -92,7 +93,13 @@ PlatformImpl::PlatformImpl(ol_platform_handle_t Platform, size_t PlatformIndex, Device, *this, DeviceImpl::PrivateTag{})); }); - MDefaultContext = ContextImpl::create(*this); + std::vector DeviceImpls; + DeviceImpls.reserve(MRootDevices.size()); + for (const auto &Device : MRootDevices) + DeviceImpls.push_back(Device.get()); + + MDefaultContext = ContextImpl::create(std::move(DeviceImpls), + defaultAsyncHandler, property_list{}); } const std::vector &PlatformImpl::getRootDevices() const { diff --git a/libsycl/src/detail/queue_impl.cpp b/libsycl/src/detail/queue_impl.cpp index 6116e2d787e6..430c0eb302f7 100644 --- a/libsycl/src/detail/queue_impl.cpp +++ b/libsycl/src/detail/queue_impl.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -66,6 +67,8 @@ QueueImpl::QueueImpl(DeviceImpl &deviceImpl, const async_handler &asyncHandler, : MIsInorder(false), MAsyncHandler(asyncHandler), MPropList(propList), MDevice(deviceImpl), MContext(MDevice.getPlatformImpl().getDefaultContext()) { + assert(MContext.getOLHandleRef() && + "Queue must be associated with a valid offload context"); callAndThrow(olCreateQueue, MDevice.getOLHandle(), &MOffloadQueue); } diff --git a/libsycl/unittests/mock/helpers.cpp b/libsycl/unittests/mock/helpers.cpp index 6b4460cd6611..b430be7b3539 100644 --- a/libsycl/unittests/mock/helpers.cpp +++ b/libsycl/unittests/mock/helpers.cpp @@ -181,6 +181,33 @@ void mock::MockLiboffload::initDefault() { return OL_SUCCESS; }); + ON_CALL(*this, olCreateContext) + .WillByDefault([this](size_t NumDevices, ol_device_handle_t *Devices, + ol_context_handle_t *Context) -> ol_result_t { + if (!Devices || !Context) + return makeEmptyStrError(OL_ERRC_INVALID_NULL_POINTER); + if (NumDevices == 0) + return makeEmptyStrError(OL_ERRC_INVALID_SIZE); + for (size_t I = 0; I < NumDevices; ++I) { + if (!Devices[I]) + return makeEmptyStrError(OL_ERRC_INVALID_NULL_HANDLE); + } + + // Preserve the first device in payload for tests that may need to + // inspect what device set the context was created from. + *Context = mock::createDummyHandleWithData( + reinterpret_cast(&Devices[0]), sizeof(Devices[0])); + return OL_SUCCESS; + }); + + ON_CALL(*this, olDestroyContext) + .WillByDefault([this](ol_context_handle_t Context) -> ol_result_t { + if (!Context) + return makeEmptyStrError(OL_ERRC_INVALID_NULL_HANDLE); + mock::releaseDummyHandle(Context); + return OL_SUCCESS; + }); + ON_CALL(*this, olCreateProgram) .WillByDefault([this](ol_device_handle_t Device, const void *ProgData, size_t ProgDataSize, diff --git a/libsycl/unittests/mock/helpers.hpp b/libsycl/unittests/mock/helpers.hpp index 0690970f1f99..2435a49b22f2 100644 --- a/libsycl/unittests/mock/helpers.hpp +++ b/libsycl/unittests/mock/helpers.hpp @@ -89,6 +89,10 @@ public: MOCK_METHOD(ol_result_t, olIterateDevices, (ol_device_iterate_cb_t Callback, void *UserData)); MOCK_METHOD(ol_result_t, olDestroyProgram, (ol_program_handle_t Program)); + MOCK_METHOD(ol_result_t, olCreateContext, + (size_t NumDevices, ol_device_handle_t *Devices, + ol_context_handle_t *Context)); + MOCK_METHOD(ol_result_t, olDestroyContext, (ol_context_handle_t Context)); MOCK_METHOD(ol_result_t, olCreateQueue, (ol_device_handle_t Device, ol_queue_handle_t *Queue)); MOCK_METHOD(ol_result_t, olDestroyQueue, (ol_queue_handle_t Queue)); diff --git a/libsycl/unittests/mock/mock.cpp b/libsycl/unittests/mock/mock.cpp index 8f5db4b5cb6e..0a2eefb887c4 100644 --- a/libsycl/unittests/mock/mock.cpp +++ b/libsycl/unittests/mock/mock.cpp @@ -50,6 +50,16 @@ ol_result_t olDestroyProgram(ol_program_handle_t Program) { return mock::getMockLiboffload().olDestroyProgram(Program); } +ol_result_t olCreateContext(size_t NumDevices, ol_device_handle_t *Devices, + ol_context_handle_t *Context) { + return mock::getMockLiboffload().olCreateContext(NumDevices, Devices, + Context); +} + +ol_result_t olDestroyContext(ol_context_handle_t Context) { + return mock::getMockLiboffload().olDestroyContext(Context); +} + ol_result_t olCreateQueue(ol_device_handle_t Device, ol_queue_handle_t *Queue) { return mock::getMockLiboffload().olCreateQueue(Device, Queue); }