blob: 0ef13295d513d0bf7d11e6176dc7eb5faf408b3b [file] [edit]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include <detail/context_impl.hpp>
#include <detail/global_objects.hpp>
#include <detail/platform_impl.hpp>
#include <detail/program_manager.hpp>
#include <detail/queue_impl.hpp>
#ifdef _WIN32
# include <windows.h>
#endif
#include <cassert>
#include <tuple>
#include <utility>
#include <vector>
_LIBSYCL_BEGIN_NAMESPACE_SYCL
namespace detail {
// libsycl follows SYCL 2020 specification that doesn't declare any
// init/shutdown methods that can help to avoid usage of static variables.
// liboffload uses static variables too. In the first call of get_platforms
// we call liboffload's iterateDevices that leads to liboffload static
// storage initialization. Then we initialize our own local static var of
// StaticVarShutdownHandler type to be able to call our shutdown methods
// earlier and before the liboffload objects are destructed at the end of
// program. See documentation of std::exit for local objects with static
// storage duration.
struct StaticVarShutdownHandler {
StaticVarShutdownHandler(const StaticVarShutdownHandler &) = delete;
StaticVarShutdownHandler &
operator=(const StaticVarShutdownHandler &) = delete;
~StaticVarShutdownHandler() {
ProgramAndKernelManager::getInstance().releaseResources();
// No error reporting in shutdown
std::ignore = olShutDown();
}
};
void registerStaticVarShutdownHandler() {
// Touch the program manager singleton first: static objects are destroyed in
// reverse order of construction, so this guarantees it is still alive when
// ~StaticVarShutdownHandler() calls releaseResources() on it.
std::ignore = ProgramAndKernelManager::getInstance();
static StaticVarShutdownHandler handler{};
}
std::array<detail::OffloadTopology, OL_PLATFORM_BACKEND_LAST> &
getOffloadTopologies() {
static std::array<detail::OffloadTopology, OL_PLATFORM_BACKEND_LAST>
Topologies{};
return Topologies;
}
std::vector<PlatformImplUPtr> &getPlatformCache() {
static std::vector<PlatformImplUPtr> PlatformCache{};
return PlatformCache;
}
InstanceWithLock<AsyncExceptionsContainer> &getAsyncExceptionList() {
static InstanceWithLock<AsyncExceptionsContainer> AsyncExceptionList;
return AsyncExceptionList;
}
void recordAsyncException(const std::shared_ptr<QueueImpl> &QueuePtr,
const std::exception_ptr &ExceptionPtr) {
assert(QueuePtr && "Queue impl ptr can't be nullptr");
AsyncExceptionKey Key{QueuePtr, QueuePtr->getContextWeakPtr()};
auto &[AsyncExceptions, AsyncExceptionsMutex] = getAsyncExceptionList();
std::lock_guard<SpinLock> Lock(AsyncExceptionsMutex);
addAsyncException(AsyncExceptions[std::move(Key)], ExceptionPtr);
}
void flushAsyncExceptions() {
auto &[AsyncExceptions, AsyncExceptionsMutex] = getAsyncExceptionList();
AsyncExceptionsContainer AsyncExceptionsSwap;
{
std::lock_guard<SpinLock> Lock(AsyncExceptionsMutex);
std::swap(AsyncExceptions, AsyncExceptionsSwap);
}
for (auto &[EntryKey, ExceptionList] : AsyncExceptionsSwap) {
exception_list Exceptions = std::move(ExceptionList);
if (Exceptions.size() == 0)
continue;
// SYCL 2020 4.13.1.3. Priorities of async handlers: the handler the queue
// was constructed with comes first, the handler of the context enclosing
// the queue comes next.
const auto &[WeakQueue, WeakContext] = EntryKey;
if (std::shared_ptr<QueueImpl> Queue = WeakQueue.lock();
Queue && Queue->getAsyncHandler()) {
Queue->getAsyncHandler()(std::move(Exceptions));
continue;
}
if (std::shared_ptr<ContextImpl> Context = WeakContext.lock();
Context && Context->get_async_handler()) {
Context->get_async_handler()(std::move(Exceptions));
continue;
}
// Neither the queue nor the context has a handler, or both of them are
// dead. A context constructed without an async_handler is given the default
// one at construction, so there is no need for a context to carry an empty
// handler: leaving it empty would end up here with an identical result.
defaultAsyncHandler(std::move(Exceptions));
}
}
} // namespace detail
_LIBSYCL_END_NAMESPACE_SYCL