From c8ea046f982a230a03d11ae26463f41defddbe20 Mon Sep 17 00:00:00 2001 From: Zheming Jin Date: Sun, 19 Jul 2026 10:52:04 -0700 Subject: [PATCH] [SYCL][HIP] Fix crash from SYCL buffer destructors during shutdown Static SYCL buffer destructors run after the runtime has begun shutting down (their atexit handlers are registered before the runtime's). On HIP this crashed twice: waiting on events whose stream/context was already released segfaults inside libamdhip64, and releasing those events returns hipErrorContextIsDestroyed which was thrown out of the event_impl destructor. - Scheduler::removeMemoryObject: extend the existing Windows-only "skip wait during shutdown" guard to all platforms. Host memory is not written back during shutdown anyway, so the wait serves no purpose and is unsafe against partially torn-down adapters. - HIP adapter event release(): treat hipErrorContextIsDestroyed and hipErrorDeinitialized as a successful release instead of surfacing an exception. Re-enables Regression/static-buffer-dtor.cpp on HIP. Co-authored-by: Cursor --- sycl/source/detail/scheduler/scheduler.cpp | 14 ++++++------- .../Regression/static-buffer-dtor.cpp | 4 ---- unified-runtime/source/adapters/hip/event.cpp | 20 ++++++++++++++++--- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index 75b1a3c7e605a..f9f27d2c86155 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -293,18 +293,18 @@ bool Scheduler::removeMemoryObject(detail::SYCLMemObjI *MemObj, // No operations were performed on the mem object return true; -#ifdef _WIN32 - // If we are shutting down on Windows it may not be - // safe to wait on host threads, as the OS may - // abandon them. But no worries, the memory WILL be reclaimed. + // If we are shutting down it may not be safe to wait on in-flight work. + // On Windows the OS may have abandoned host threads. On any platform the + // adapters/contexts may already be partially torn down by early shutdown + // (e.g. the HIP runtime segfaults when synchronizing on an event whose + // stream/context has been released). During shutdown host memory is not + // written back either (see SYCLMemObjT::updateHostMemory), so waiting serves + // no purpose here and the memory WILL be reclaimed as the process exits. bool allowWait = MemObj->hasUserDataPtr() || GlobalHandler::instance().isOkToDefer(); if (!allowWait) { StrictLock = false; } -#else - bool allowWait = true; -#endif if (allowWait) { // This only needs a shared mutex as it only involves enqueueing and diff --git a/sycl/test-e2e/Regression/static-buffer-dtor.cpp b/sycl/test-e2e/Regression/static-buffer-dtor.cpp index 82633f4cfb047..d16616f3fdf35 100644 --- a/sycl/test-e2e/Regression/static-buffer-dtor.cpp +++ b/sycl/test-e2e/Regression/static-buffer-dtor.cpp @@ -12,10 +12,6 @@ // RUN: %{build} -o %t.out // RUN: %{run} %t.out -// Failing on HIP AMD -// UNSUPPORTED: hip -// UNSUPPORTED-TRACKER: https://github.com/intel/llvm/issues/22300 - #include int main() { diff --git a/unified-runtime/source/adapters/hip/event.cpp b/unified-runtime/source/adapters/hip/event.cpp index 6c0c49a729cff..1d6fb3f5c8a9f 100644 --- a/unified-runtime/source/adapters/hip/event.cpp +++ b/unified-runtime/source/adapters/hip/event.cpp @@ -53,11 +53,25 @@ ur_result_t ur_event_handle_t_::release() { return UR_RESULT_SUCCESS; assert(Queue != nullptr); - UR_CHECK_ERROR(hipEventDestroy(EvEnd)); + + // During runtime/process shutdown the HIP context may already have been + // destroyed (e.g. static SYCL buffer destructors that run after the SYCL + // runtime released its contexts). The backing events are torn down together + // with the context, so treat these shutdown-specific errors as a successful + // release rather than surfacing an exception (which would otherwise abort in + // the event_impl destructor). + auto destroyEvent = [](hipEvent_t Event) { + hipError_t Res = hipEventDestroy(Event); + if (Res == hipErrorContextIsDestroyed || Res == hipErrorDeinitialized) + return; + UR_CHECK_ERROR(Res); + }; + + destroyEvent(EvEnd); if (HasProfiling) { - UR_CHECK_ERROR(hipEventDestroy(EvQueued)); - UR_CHECK_ERROR(hipEventDestroy(EvStart)); + destroyEvent(EvQueued); + destroyEvent(EvStart); } return UR_RESULT_SUCCESS;