-
Notifications
You must be signed in to change notification settings - Fork 252
Fix pool_memory_resource crash during process exit #2367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
8660942
Fix pool_memory_resource crash during process exit
bdice 03d4805
Move shutdown-safety contract onto per-device resource setters
bdice 68ebf54
Match rmm doxygen style
bdice 8bd42cc
Add tests for process_is_exiting()
bdice 561ce68
Narrow shutdown guards and simplify cleanup paths
bdice 573dd78
Simplify runtime shutdown registration
bdice 6ecc14b
Narrow shutdown docs and strengthen tests
bdice 4a814f3
Document shutdown support is limited to the per-device map
bdice 520d6f9
Abort if atexit registration fails
bdice 963e54a
Address reviews
bdice File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #pragma once | ||
|
|
||
| #include <rmm/detail/export.hpp> | ||
|
|
||
| namespace RMM_NAMESPACE { | ||
| namespace detail { | ||
|
|
||
| /** | ||
| * @brief Register the atexit callback that flips the flag observed by `rmm::process_is_exiting()`. | ||
| * | ||
| * This registers the single process-exit hook used to make resources held in RMM's internal | ||
| * per-device resource map safe to destruct during process termination. It is not a general | ||
| * per-static-object registration facility. | ||
| */ | ||
| RMM_EXPORT void register_process_exit_hook() noexcept; | ||
|
|
||
| } // namespace detail | ||
| } // namespace RMM_NAMESPACE | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #pragma once | ||
|
|
||
| #include <rmm/detail/export.hpp> | ||
|
|
||
| namespace RMM_EXPORT rmm { | ||
|
|
||
| /** | ||
| * @addtogroup memory_resources | ||
| * @{ | ||
| * @file | ||
| */ | ||
|
|
||
| /** | ||
| * @brief Returns `true` if the process has entered `exit()` / atexit handler execution. | ||
| * | ||
| * Destructors of static objects, as well as atexit handlers registered by other DSOs, run | ||
| * during process termination after `main()` has returned. At that point calling into the CUDA | ||
| * runtime or driver is undefined behavior: the primary context may already be destroyed, and | ||
| * CUDA API calls may dereference released state and crash inside libcuda rather than returning | ||
| * an error. | ||
| * | ||
| * Use this function from a memory resource destructor (or a helper invoked by a destructor, such | ||
| * as a `release()` method) when the resource may be held in RMM's internal per-device resource | ||
| * map and destroyed during process termination. In that case the destructor may run after the | ||
| * CUDA primary context has been destroyed, and calling into the CUDA runtime is undefined | ||
| * behavior. Destructors can avoid that by: | ||
| * | ||
| * 1. Never calling CUDA APIs from the destructor at all, or | ||
| * 2. Consulting `rmm::process_is_exiting()` in the destructor (and in any helper invoked by | ||
| * the destructor, such as a `release()` method) and skipping CUDA API calls when it | ||
| * returns `true`. In that case, resources that would have been explicitly released should be | ||
| * leaked; the OS reclaims them when the process exits. | ||
| * | ||
| * Storing RMM objects with static or thread-local scope is unsupported. Users should not create | ||
| * their own static containers of RMM objects and rely on `rmm::process_is_exiting()` to make | ||
| * those destructors safe. | ||
| * | ||
| * Calling `rmm::process_is_exiting()` from a resource destructor is always safe: it performs a | ||
| * single atomic load (acquire semantics) and never calls into CUDA. | ||
| * | ||
| * Example: | ||
| * @code{.cpp} | ||
| * class my_resource final : public ... { | ||
| * ~my_resource() override | ||
| * { | ||
| * if (rmm::process_is_exiting()) { | ||
| * return; | ||
| * } | ||
| * RMM_ASSERT_CUDA_SUCCESS_SAFE_SHUTDOWN(cudaFree(ptr_)); | ||
| * } | ||
| * }; | ||
| * @endcode | ||
| * | ||
| * @return `true` if `exit()` has begun; `false` otherwise. | ||
| */ | ||
| bool process_is_exiting() noexcept; | ||
|
|
||
| /** @} */ // end of group | ||
|
|
||
| } // namespace RMM_EXPORT rmm |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <rmm/detail/error.hpp> | ||
| #include <rmm/detail/runtime_shutdown.hpp> | ||
| #include <rmm/process_is_exiting.hpp> | ||
|
|
||
| #include <atomic> | ||
| #include <cstdlib> | ||
| #include <mutex> | ||
|
|
||
| namespace RMM_NAMESPACE { | ||
|
|
||
| namespace { | ||
|
|
||
|
bdice marked this conversation as resolved.
|
||
| std::atomic<bool> exiting{false}; | ||
| std::once_flag registered; | ||
|
|
||
| } // namespace | ||
|
|
||
| bool process_is_exiting() noexcept { return exiting.load(std::memory_order_acquire); } | ||
|
|
||
| namespace detail { | ||
|
|
||
| void register_process_exit_hook() noexcept | ||
| { | ||
| // The C++ standard guarantees that if the completion of the initialization of a static object A | ||
| // is sequenced-before a call to std::atexit(F), then F runs before A's destructor at | ||
| // termination. RMM exploits this by registering this flag-setter immediately after the internal | ||
| // per-device resource map is constructed, so the flag is observed as true during that map's | ||
| // destructor. | ||
| std::call_once(registered, []() { | ||
| auto const registration_status = | ||
| std::atexit([]() noexcept { exiting.store(true, std::memory_order_release); }); | ||
| RMM_EXPECTS(registration_status == 0, "Unable to register process-exit hook"); | ||
| }); | ||
| } | ||
|
|
||
| } // namespace detail | ||
| } // namespace RMM_NAMESPACE | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <rmm/mr/per_device_resource.hpp> | ||
| #include <rmm/process_is_exiting.hpp> | ||
|
|
||
| #include <cstdlib> | ||
|
|
||
| /* | ||
| * Standalone test that verifies rmm::process_is_exiting() reports true while the C runtime is | ||
| * executing atexit handlers. Resource destructors running during exit() must observe the flag | ||
| * as set so that they can skip CUDA calls. | ||
| * | ||
| * This binary exercises the real integration point without any CUDA involvement: | ||
| * | ||
| * 1. main() registers `checker` via std::atexit FIRST. | ||
| * 2. main() then calls detail::get_ref_map(). This constructs the static per-device map and, | ||
| * immediately afterward, calls register_process_exit_hook(), which registers the internal | ||
| * flag-setter via std::atexit SECOND. | ||
| * 3. main() returns 0. | ||
| * 4. At termination, atexit handlers run in LIFO order: first the flag-setter (sets the | ||
| * flag to true), then `checker` (reads the flag). | ||
| * 5. If `checker` observes the flag as true, the process exits with status 0; otherwise it | ||
| * calls _Exit(1) to signal failure. | ||
| * | ||
| * CTest checks the exit status. | ||
| */ | ||
|
|
||
| namespace { | ||
|
|
||
| void checker() noexcept | ||
| { | ||
| if (!rmm::process_is_exiting()) { | ||
| // The process is already running atexit handlers. Use std::_Exit rather than std::exit to | ||
| // report failure without re-entering normal termination or running more cleanup. | ||
| std::_Exit(1); | ||
| } | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| int main() | ||
| { | ||
| // Register the checker before get_ref_map() so that the checker runs AFTER the flag-setter at | ||
| // termination. | ||
| if (std::atexit(checker) != 0) { return 2; } | ||
| [[maybe_unused]] auto& map = rmm::mr::detail::get_ref_map(); | ||
| return 0; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.