@@ -37,9 +37,11 @@ using LockGuard = std::lock_guard<SpinLock>;
37
37
SpinLock GlobalHandler::MSyclGlobalHandlerProtector{};
38
38
39
39
// forward decl
40
- void shutdown_win (); // TODO: win variant will go away soon
41
40
void shutdown_early ();
42
41
void shutdown_late ();
42
+ #ifdef _WIN32
43
+ BOOL isLinkedStatically ();
44
+ #endif
43
45
44
46
// Utility class to track references on object.
45
47
// Used for GlobalHandler now and created as thread_local object on the first
@@ -60,10 +62,6 @@ class ObjectUsageCounter {
60
62
61
63
LockGuard Guard (GlobalHandler::MSyclGlobalHandlerProtector);
62
64
MCounter--;
63
- GlobalHandler *RTGlobalObjHandler = GlobalHandler::getInstancePtr ();
64
- if (RTGlobalObjHandler) {
65
- RTGlobalObjHandler->prepareSchedulerToRelease (!MCounter);
66
- }
67
65
} catch (std::exception &e) {
68
66
__SYCL_REPORT_EXCEPTION_TO_STREAM (" exception in ~ObjectUsageCounter" , e);
69
67
}
@@ -254,24 +252,37 @@ void GlobalHandler::releaseDefaultContexts() {
254
252
MPlatformToDefaultContextCache.Inst .reset (nullptr );
255
253
}
256
254
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 () {
259
265
try {
260
266
#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 ();
263
274
#else
264
275
shutdown_early ();
265
276
#endif
266
277
} 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);
269
280
}
270
281
}
271
282
};
272
283
273
- void GlobalHandler::registerEarlyShutdownHandler () {
274
- static EarlyShutdownHandler handler{};
284
+ void GlobalHandler::registerStaticVarShutdownHandler () {
285
+ static StaticVarShutdownHandler handler{};
275
286
}
276
287
277
288
bool GlobalHandler::isOkToDefer () const { return OkToDefer; }
@@ -304,35 +315,29 @@ void GlobalHandler::prepareSchedulerToRelease(bool Blocking) {
304
315
#ifndef _WIN32
305
316
if (Blocking)
306
317
drainThreadPool ();
318
+ #endif
307
319
if (MScheduler.Inst )
308
320
MScheduler.Inst ->releaseResources (Blocking ? BlockingT::BLOCKING
309
321
: BlockingT::NON_BLOCKING);
310
- #endif
311
322
}
312
323
313
324
void GlobalHandler::drainThreadPool () {
314
325
if (MHostTaskThreadPool.Inst )
315
326
MHostTaskThreadPool.Inst ->drain ();
316
327
}
317
328
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
330
329
void shutdown_early () {
331
330
const LockGuard Lock{GlobalHandler::MSyclGlobalHandlerProtector};
332
331
GlobalHandler *&Handler = GlobalHandler::getInstancePtr ();
333
332
if (!Handler)
334
333
return ;
335
334
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
+
336
341
// Now that we are shutting down, we will no longer defer MemObj releases.
337
342
Handler->endDeferredRelease ();
338
343
@@ -354,6 +359,12 @@ void shutdown_late() {
354
359
if (!Handler)
355
360
return ;
356
361
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
+
357
368
// First, release resources, that may access adapters.
358
369
Handler->MPlatformCache .Inst .reset (nullptr );
359
370
Handler->MScheduler .Inst .reset (nullptr );
@@ -370,7 +381,6 @@ void shutdown_late() {
370
381
delete Handler;
371
382
Handler = nullptr ;
372
383
}
373
- #endif
374
384
375
385
#ifdef _WIN32
376
386
extern " C" __SYCL_EXPORT BOOL WINAPI DllMain (HINSTANCE hinstDLL,
@@ -391,23 +401,18 @@ extern "C" __SYCL_EXPORT BOOL WINAPI DllMain(HINSTANCE hinstDLL,
391
401
if (PrintUrTrace)
392
402
std::cout << " ---> DLL_PROCESS_DETACH syclx.dll\n " << std::endl;
393
403
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
-
401
404
try {
402
- shutdown_win ();
405
+ shutdown_early ();
403
406
} 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);
405
408
return FALSE ;
406
409
}
410
+
407
411
break ;
408
412
case DLL_PROCESS_ATTACH:
409
413
if (PrintUrTrace)
410
414
std::cout << " ---> DLL_PROCESS_ATTACH syclx.dll\n " << std::endl;
415
+
411
416
break ;
412
417
case DLL_THREAD_ATTACH:
413
418
break ;
@@ -416,6 +421,28 @@ extern "C" __SYCL_EXPORT BOOL WINAPI DllMain(HINSTANCE hinstDLL,
416
421
}
417
422
return TRUE ; // Successful DLL_PROCESS_ATTACH.
418
423
}
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
+ }
419
446
#else
420
447
// Setting low priority on destructor ensures it runs after all other global
421
448
// destructors. Priorities 0-100 are reserved by the compiler. The priority
0 commit comments