@@ -28,17 +28,18 @@ namespace sycl {
2828__SYCL_INLINE_VER_NAMESPACE (_V1) {
2929namespace 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 {
3741public:
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
5462private:
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
6468GlobalHandler::GlobalHandler () = default ;
6569GlobalHandler::~GlobalHandler () = default ;
6670
71+ GlobalHandler *&GlobalHandler::getInstancePtr () {
72+ static GlobalHandler *RTGlobalObjHandler = new GlobalHandler ();
73+ return RTGlobalObjHandler;
74+ }
75+
6776GlobalHandler &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
7282template <typename T, typename ... Types>
@@ -94,8 +104,7 @@ Scheduler &GlobalHandler::getScheduler() {
94104}
95105
96106void GlobalHandler::registerSchedulerUsage (bool ModifyCounter) {
97- thread_local ObjectUsageCounter<Scheduler> SchedulerCounter (MScheduler.Inst ,
98- ModifyCounter);
107+ thread_local ObjectUsageCounter SchedulerCounter (ModifyCounter);
99108}
100109
101110ProgramManager &GlobalHandler::getProgramManager () {
@@ -151,14 +160,14 @@ ThreadPool &GlobalHandler::getHostTaskThreadPool() {
151160void 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
195204void GlobalHandler::drainThreadPool () {
@@ -198,34 +207,40 @@ void GlobalHandler::drainThreadPool() {
198207}
199208
200209void 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
0 commit comments