[libsycl] Add liboffload context handler to default context (#213249)

Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova@intel.com>
This commit is contained in:
Kseniya Tikhomirova 2026-07-31 16:46:12 +02:00 committed by GitHub
parent 8719d58faf
commit 1505b3e83d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 115 additions and 19 deletions

View file

@ -13,15 +13,41 @@ _LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
void ContextImpl::iterateDevices(
const std::function<void(DeviceImpl *)> &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<DeviceImpl *> &&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<ol_device_handle_t> 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<void(DeviceImpl *)> &callback) const {
for (DeviceImpl *Device : MDevices)
callback(Device);
}
backend ContextImpl::getBackend() const { return MDevices[0]->getBackend(); }
} // namespace detail
_LIBSYCL_END_NAMESPACE_SYCL

View file

@ -15,6 +15,7 @@
#ifndef _LIBSYCL_CONTEXT_IMPL
#define _LIBSYCL_CONTEXT_IMPL
#include <sycl/__impl/async_handler.hpp>
#include <sycl/__impl/context.hpp>
#include <sycl/__impl/detail/config.hpp>
@ -23,16 +24,14 @@
#include <functional>
_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<ContextImpl> {
@ -41,12 +40,22 @@ class ContextImpl : public std::enable_shared_from_this<ContextImpl> {
};
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<DeviceImpl *> &&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<ContextImpl>(std::forward<Ts>(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<DeviceImpl *> MDevices;
ol_context_handle_t MOffloadContext{};
};
} // namespace detail

View file

@ -8,6 +8,7 @@
#include <sycl/__impl/detail/config.hpp>
#include <sycl/__impl/detail/obj_utils.hpp>
#include <sycl/__impl/property_list.hpp>
#include <detail/context_impl.hpp>
#include <detail/device_impl.hpp>
@ -92,7 +93,13 @@ PlatformImpl::PlatformImpl(ol_platform_handle_t Platform, size_t PlatformIndex,
Device, *this, DeviceImpl::PrivateTag{}));
});
MDefaultContext = ContextImpl::create(*this);
std::vector<DeviceImpl *> 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<DeviceImplUPtr> &PlatformImpl::getRootDevices() const {

View file

@ -8,6 +8,7 @@
#include <detail/queue_impl.hpp>
#include <detail/context_impl.hpp>
#include <detail/device_impl.hpp>
#include <detail/event_impl.hpp>
#include <detail/global_objects.hpp>
@ -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);
}

View file

@ -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<ol_context_handle_t>(
reinterpret_cast<unsigned char *>(&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,

View file

@ -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));

View file

@ -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);
}