@@ -37,9 +37,11 @@ using LockGuard = std::lock_guard<SpinLock>;
3737SpinLock GlobalHandler::MSyclGlobalHandlerProtector{};
3838
3939// forward decl
40- void shutdown_win (); // TODO: win variant will go away soon
4140void shutdown_early ();
4241void shutdown_late ();
42+ #ifdef _WIN32
43+ BOOL isLinkedStatically ();
44+ #endif
4345
4446// Utility class to track references on object.
4547// Used for GlobalHandler now and created as thread_local object on the first
@@ -60,10 +62,6 @@ class ObjectUsageCounter {
6062
6163 LockGuard Guard (GlobalHandler::MSyclGlobalHandlerProtector);
6264 MCounter--;
63- GlobalHandler *RTGlobalObjHandler = GlobalHandler::getInstancePtr ();
64- if (RTGlobalObjHandler) {
65- RTGlobalObjHandler->prepareSchedulerToRelease (!MCounter);
66- }
6765 } catch (std::exception &e) {
6866 __SYCL_REPORT_EXCEPTION_TO_STREAM (" exception in ~ObjectUsageCounter" , e);
6967 }
@@ -254,24 +252,37 @@ void GlobalHandler::releaseDefaultContexts() {
254252 MPlatformToDefaultContextCache.Inst .reset (nullptr );
255253}
256254
257- struct EarlyShutdownHandler {
258- ~EarlyShutdownHandler () {
255+ // Shutdown is split into two parts. shutdown_early() stops any more
256+ // objects from being deferred and takes an initial pass at freeing them.
257+ // shutdown_late() finishes and releases the adapters and the GlobalHandler.
258+ // For Windows, early shutdown is typically called from DllMain,
259+ // and late shutdown is here.
260+ // For Linux, early shutdown is here, and late shutdown is called from
261+ // a low priority destructor.
262+ struct StaticVarShutdownHandler {
263+
264+ ~StaticVarShutdownHandler () {
259265 try {
260266#ifdef _WIN32
261- // on Windows we keep to the existing shutdown procedure
262- GlobalHandler::instance ().releaseDefaultContexts ();
267+ // If statically linked, DllMain will not be called. So we do its work
268+ // here.
269+ if (isLinkedStatically ()) {
270+ shutdown_early ();
271+ }
272+
273+ shutdown_late ();
263274#else
264275 shutdown_early ();
265276#endif
266277 } catch (std::exception &e) {
267- __SYCL_REPORT_EXCEPTION_TO_STREAM (" exception in ~EarlyShutdownHandler " ,
268- e);
278+ __SYCL_REPORT_EXCEPTION_TO_STREAM (
279+ " exception in ~StaticVarShutdownHandler " , e);
269280 }
270281 }
271282};
272283
273- void GlobalHandler::registerEarlyShutdownHandler () {
274- static EarlyShutdownHandler handler{};
284+ void GlobalHandler::registerStaticVarShutdownHandler () {
285+ static StaticVarShutdownHandler handler{};
275286}
276287
277288bool GlobalHandler::isOkToDefer () const { return OkToDefer; }
@@ -304,35 +315,29 @@ void GlobalHandler::prepareSchedulerToRelease(bool Blocking) {
304315#ifndef _WIN32
305316 if (Blocking)
306317 drainThreadPool ();
318+ #endif
307319 if (MScheduler.Inst )
308320 MScheduler.Inst ->releaseResources (Blocking ? BlockingT::BLOCKING
309321 : BlockingT::NON_BLOCKING);
310- #endif
311322}
312323
313324void GlobalHandler::drainThreadPool () {
314325 if (MHostTaskThreadPool.Inst )
315326 MHostTaskThreadPool.Inst ->drain ();
316327}
317328
318- #ifdef _WIN32
319- // because of something not-yet-understood on Windows
320- // threads may be shutdown once the end of main() is reached
321- // making an orderly shutdown difficult. Fortunately, Windows
322- // itself is very aggressive about reclaiming memory. Thus,
323- // we focus solely on unloading the adapters, so as to not
324- // accidentally retain device handles. etc
325- void shutdown_win () {
326- GlobalHandler *&Handler = GlobalHandler::getInstancePtr ();
327- Handler->unloadAdapters ();
328- }
329- #else
330329void shutdown_early () {
331330 const LockGuard Lock{GlobalHandler::MSyclGlobalHandlerProtector};
332331 GlobalHandler *&Handler = GlobalHandler::getInstancePtr ();
333332 if (!Handler)
334333 return ;
335334
335+ #if defined(XPTI_ENABLE_INSTRUMENTATION) && defined(_WIN32)
336+ if (xptiTraceEnabled ())
337+ return ; // When doing xpti tracing, we can't safely shutdown on Win.
338+ // TODO: figure out why XPTI prevents release.
339+ #endif
340+
336341 // Now that we are shutting down, we will no longer defer MemObj releases.
337342 Handler->endDeferredRelease ();
338343
@@ -354,6 +359,12 @@ void shutdown_late() {
354359 if (!Handler)
355360 return ;
356361
362+ #if defined(XPTI_ENABLE_INSTRUMENTATION) && defined(_WIN32)
363+ if (xptiTraceEnabled ())
364+ return ; // When doing xpti tracing, we can't safely shutdown on Win.
365+ // TODO: figure out why XPTI prevents release.
366+ #endif
367+
357368 // First, release resources, that may access adapters.
358369 Handler->MPlatformCache .Inst .reset (nullptr );
359370 Handler->MScheduler .Inst .reset (nullptr );
@@ -370,7 +381,6 @@ void shutdown_late() {
370381 delete Handler;
371382 Handler = nullptr ;
372383}
373- #endif
374384
375385#ifdef _WIN32
376386extern " C" __SYCL_EXPORT BOOL WINAPI DllMain (HINSTANCE hinstDLL,
@@ -391,23 +401,18 @@ extern "C" __SYCL_EXPORT BOOL WINAPI DllMain(HINSTANCE hinstDLL,
391401 if (PrintUrTrace)
392402 std::cout << " ---> DLL_PROCESS_DETACH syclx.dll\n " << std::endl;
393403
394- #ifdef XPTI_ENABLE_INSTRUMENTATION
395- if (xptiTraceEnabled ())
396- return TRUE ; // When doing xpti tracing, we can't safely call shutdown.
397- // TODO: figure out what XPTI is doing that prevents
398- // release.
399- #endif
400-
401404 try {
402- shutdown_win ();
405+ shutdown_early ();
403406 } catch (std::exception &e) {
404- __SYCL_REPORT_EXCEPTION_TO_STREAM (" exception in shutdown_win " , e);
407+ __SYCL_REPORT_EXCEPTION_TO_STREAM (" exception in DLL_PROCESS_DETACH " , e);
405408 return FALSE ;
406409 }
410+
407411 break ;
408412 case DLL_PROCESS_ATTACH:
409413 if (PrintUrTrace)
410414 std::cout << " ---> DLL_PROCESS_ATTACH syclx.dll\n " << std::endl;
415+
411416 break ;
412417 case DLL_THREAD_ATTACH:
413418 break ;
@@ -416,6 +421,28 @@ extern "C" __SYCL_EXPORT BOOL WINAPI DllMain(HINSTANCE hinstDLL,
416421 }
417422 return TRUE ; // Successful DLL_PROCESS_ATTACH.
418423}
424+ BOOL isLinkedStatically () {
425+ // If the exePath is the same as the dllPath,
426+ // or if the module handle for DllMain is not retrievable,
427+ // then we are linked statically
428+ // Otherwise we are dynamically linked or loaded.
429+ HMODULE hModule = nullptr ;
430+ auto LpModuleAddr = reinterpret_cast <LPCSTR>(&DllMain);
431+ if (!GetModuleHandleExA (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, LpModuleAddr,
432+ &hModule)) {
433+ return true ; // not retrievable, therefore statically linked
434+ }
435+ char dllPath[MAX_PATH];
436+ if (GetModuleFileNameA (hModule, dllPath, MAX_PATH)) {
437+ char exePath[MAX_PATH];
438+ if (GetModuleFileNameA (NULL , exePath, MAX_PATH)) {
439+ if (std::string (dllPath) == std::string (exePath)) {
440+ return true ; // paths identical, therefore statically linked
441+ }
442+ }
443+ }
444+ return false ; // Otherwise dynamically linked or loaded
445+ }
419446#else
420447// Setting low priority on destructor ensures it runs after all other global
421448// destructors. Priorities 0-100 are reserved by the compiler. The priority
0 commit comments