Skip to content

Commit ac58dd3

Browse files
[SYCL] Fix segfault on program exit when user thread is not finished yet (#7908)
(caused by #6837) Signed-off-by: Tikhomirova, Kseniya <[email protected]>
1 parent d164fd9 commit ac58dd3

File tree

3 files changed

+56
-43
lines changed

3 files changed

+56
-43
lines changed

sycl/source/detail/global_handler.cpp

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,18 @@ namespace sycl {
2828
__SYCL_INLINE_VER_NAMESPACE(_V1) {
2929
namespace detail {
3030

31+
using LockGuard = std::lock_guard<SpinLock>;
32+
SpinLock GlobalHandler::MSyclGlobalHandlerProtector{};
33+
3134
// Utility class to track references on object.
32-
// Used for Scheduler now and created as thread_local object.
33-
// Origin idea is to track usage of Scheduler from main and other used threads -
34-
// they increment MCounter; and to use but not add extra reference by our
35-
// thread_pool threads. For this control MIncrementCounter class member is used.
36-
template <class ResourceHandler> class ObjectUsageCounter {
35+
// Used for GlobalHandler now and created as thread_local object on the first
36+
// Scheduler usage. Origin idea is to track usage of Scheduler from main and
37+
// other used threads - they increment MCounter; and to use but not add extra
38+
// reference by our thread_pool threads. For this control MIncrementCounter
39+
// class member is used.
40+
class ObjectUsageCounter {
3741
public:
38-
// Note: -Wctad-maybe-unsupported may generate warning if no ResourceHandler
39-
// type explicitly declared.
40-
ObjectUsageCounter(std::unique_ptr<ResourceHandler> &Obj, bool ModifyCounter)
41-
: MModifyCounter(ModifyCounter), MObj(Obj) {
42+
ObjectUsageCounter(bool ModifyCounter) : MModifyCounter(ModifyCounter) {
4243
if (MModifyCounter)
4344
MCounter++;
4445
}
@@ -47,26 +48,35 @@ template <class ResourceHandler> class ObjectUsageCounter {
4748
return;
4849

4950
MCounter--;
50-
if (!MCounter && MObj)
51-
MObj->releaseResources();
51+
if (!MCounter) {
52+
LockGuard Guard(GlobalHandler::MSyclGlobalHandlerProtector);
53+
GlobalHandler *RTGlobalObjHandler = GlobalHandler::getInstancePtr();
54+
if (RTGlobalObjHandler) {
55+
RTGlobalObjHandler->drainThreadPool();
56+
if (RTGlobalObjHandler->MScheduler.Inst)
57+
RTGlobalObjHandler->MScheduler.Inst->releaseResources();
58+
}
59+
}
5260
}
5361

5462
private:
5563
static std::atomic_uint MCounter;
5664
bool MModifyCounter;
57-
std::unique_ptr<ResourceHandler> &MObj;
5865
};
59-
template <class ResourceHandler>
60-
std::atomic_uint ObjectUsageCounter<ResourceHandler>::MCounter{0};
61-
62-
using LockGuard = std::lock_guard<SpinLock>;
66+
std::atomic_uint ObjectUsageCounter::MCounter{0};
6367

6468
GlobalHandler::GlobalHandler() = default;
6569
GlobalHandler::~GlobalHandler() = default;
6670

71+
GlobalHandler *&GlobalHandler::getInstancePtr() {
72+
static GlobalHandler *RTGlobalObjHandler = new GlobalHandler();
73+
return RTGlobalObjHandler;
74+
}
75+
6776
GlobalHandler &GlobalHandler::instance() {
68-
static GlobalHandler *SyclGlobalObjectsHandler = new GlobalHandler();
69-
return *SyclGlobalObjectsHandler;
77+
GlobalHandler *RTGlobalObjHandler = GlobalHandler::getInstancePtr();
78+
assert(RTGlobalObjHandler && "Handler must not be deallocated earlier");
79+
return *RTGlobalObjHandler;
7080
}
7181

7282
template <typename T, typename... Types>
@@ -94,8 +104,7 @@ Scheduler &GlobalHandler::getScheduler() {
94104
}
95105

96106
void GlobalHandler::registerSchedulerUsage(bool ModifyCounter) {
97-
thread_local ObjectUsageCounter<Scheduler> SchedulerCounter(MScheduler.Inst,
98-
ModifyCounter);
107+
thread_local ObjectUsageCounter SchedulerCounter(ModifyCounter);
99108
}
100109

101110
ProgramManager &GlobalHandler::getProgramManager() {
@@ -151,14 +160,14 @@ ThreadPool &GlobalHandler::getHostTaskThreadPool() {
151160
void GlobalHandler::releaseDefaultContexts() {
152161
// Release shared-pointers to SYCL objects.
153162
#ifndef _WIN32
154-
GlobalHandler::instance().MPlatformToDefaultContextCache.Inst.reset(nullptr);
163+
MPlatformToDefaultContextCache.Inst.reset(nullptr);
155164
#else
156165
// Windows does not maintain dependencies between dynamically loaded libraries
157166
// and can unload SYCL runtime dependencies before sycl.dll's DllMain has
158167
// finished. To avoid calls to nowhere, intentionally leak platform to device
159168
// cache. This will prevent destructors from being called, thus no PI cleanup
160169
// routines will be called in the end.
161-
GlobalHandler::instance().MPlatformToDefaultContextCache.Inst.release();
170+
MPlatformToDefaultContextCache.Inst.release();
162171
#endif
163172
}
164173

@@ -178,8 +187,8 @@ void GlobalHandler::unloadPlugins() {
178187
// Call to GlobalHandler::instance().getPlugins() initializes plugins. If
179188
// user application has loaded SYCL runtime, and never called any APIs,
180189
// there's no need to load and unload plugins.
181-
if (GlobalHandler::instance().MPlugins.Inst) {
182-
for (plugin &Plugin : GlobalHandler::instance().getPlugins()) {
190+
if (MPlugins.Inst) {
191+
for (plugin &Plugin : getPlugins()) {
183192
// PluginParameter is reserved for future use that can control
184193
// some parameters in the plugin tear-down process.
185194
// Currently, it is not used.
@@ -189,7 +198,7 @@ void GlobalHandler::unloadPlugins() {
189198
}
190199
}
191200
// Clear after unload to avoid uses after unload.
192-
GlobalHandler::instance().getPlugins().clear();
201+
getPlugins().clear();
193202
}
194203

195204
void GlobalHandler::drainThreadPool() {
@@ -198,34 +207,40 @@ void GlobalHandler::drainThreadPool() {
198207
}
199208

200209
void shutdown() {
210+
const LockGuard Lock{GlobalHandler::MSyclGlobalHandlerProtector};
211+
GlobalHandler *&Handler = GlobalHandler::getInstancePtr();
212+
if (!Handler)
213+
return;
214+
201215
// Ensure neither host task is working so that no default context is accessed
202216
// upon its release
217+
Handler->drainThreadPool();
218+
if (Handler->MScheduler.Inst)
219+
Handler->MScheduler.Inst->releaseResources();
203220

204-
if (GlobalHandler::instance().MScheduler.Inst)
205-
GlobalHandler::instance().MScheduler.Inst->releaseResources();
206-
207-
if (GlobalHandler::instance().MHostTaskThreadPool.Inst)
208-
GlobalHandler::instance().MHostTaskThreadPool.Inst->finishAndWait();
221+
if (Handler->MHostTaskThreadPool.Inst)
222+
Handler->MHostTaskThreadPool.Inst->finishAndWait();
209223

210224
// If default contexts are requested after the first default contexts have
211225
// been released there may be a new default context. These must be released
212226
// prior to closing the plugins.
213227
// Note: Releasing a default context here may cause failures in plugins with
214228
// global state as the global state may have been released.
215-
GlobalHandler::instance().releaseDefaultContexts();
229+
Handler->releaseDefaultContexts();
216230

217231
// First, release resources, that may access plugins.
218-
GlobalHandler::instance().MPlatformCache.Inst.reset(nullptr);
219-
GlobalHandler::instance().MScheduler.Inst.reset(nullptr);
220-
GlobalHandler::instance().MProgramManager.Inst.reset(nullptr);
232+
Handler->MPlatformCache.Inst.reset(nullptr);
233+
Handler->MScheduler.Inst.reset(nullptr);
234+
Handler->MProgramManager.Inst.reset(nullptr);
221235

222236
// Clear the plugins and reset the instance if it was there.
223-
GlobalHandler::instance().unloadPlugins();
224-
if (GlobalHandler::instance().MPlugins.Inst)
225-
GlobalHandler::instance().MPlugins.Inst.reset(nullptr);
237+
Handler->unloadPlugins();
238+
if (Handler->MPlugins.Inst)
239+
Handler->MPlugins.Inst.reset(nullptr);
226240

227241
// Release the rest of global resources.
228-
delete &GlobalHandler::instance();
242+
delete Handler;
243+
Handler = nullptr;
229244
}
230245

231246
#ifdef _WIN32

sycl/source/detail/global_handler.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class GlobalHandler {
8383

8484
private:
8585
friend void shutdown();
86+
friend class ObjectUsageCounter;
87+
static GlobalHandler *&getInstancePtr();
88+
static SpinLock MSyclGlobalHandlerProtector;
8689

8790
// Constructor and destructor are declared out-of-line to allow incomplete
8891
// types as template arguments to unique_ptr.

sycl/source/detail/scheduler/scheduler.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,6 @@ Scheduler::~Scheduler() { DefaultHostQueue.reset(); }
392392

393393
void Scheduler::releaseResources() {
394394
#ifndef _WIN32
395-
if (DefaultHostQueue) {
396-
DefaultHostQueue->wait();
397-
}
398-
GlobalHandler::instance().drainThreadPool();
399-
400395
// There might be some commands scheduled for post enqueue cleanup that
401396
// haven't been freed because of the graph mutex being locked at the time,
402397
// clean them up now.

0 commit comments

Comments
 (0)