cuda.core: make Device methods use their bound context - #2750
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
|
/ok to test |
|
a09bebe to
838f850
Compare
Run context-sensitive Device operations against the Device's bound context while preserving caller state. Centralize context-aware cleanup and synchronous allocation handling so resource lifetimes remain correct.
838f850 to
83a3d46
Compare
|
/ok to test |
| // Run a creation operation and undo it if context restoration fails. | ||
| // Context-independent undo always runs. Context-sensitive undo runs only | ||
| // after verifying that the target context remains current; otherwise the | ||
| // resource leaks rather than risking cleanup in the wrong context. | ||
| template <typename Fn, typename Undo> | ||
| CUresult invoke_in_context_or_undo(const ContextHandle& h_context, Fn&& operation, | ||
| Undo&& undo, bool undo_requires_target_context) noexcept { | ||
| ASSERT_NOTHROW_INVOCABLE(Fn&&); | ||
| ASSERT_NOTHROW_INVOCABLE(Undo&&); | ||
| CUcontext previous = nullptr; | ||
| int changed = 0; | ||
| CUresult status = enter_context(h_context, &previous, &changed); | ||
| if (status != CUDA_SUCCESS) { | ||
| return status; | ||
| } | ||
| status = std::invoke(std::forward<Fn>(operation)); | ||
| CUresult composite = exit_context(previous, changed, status); | ||
| if (status == CUDA_SUCCESS && composite != CUDA_SUCCESS) { | ||
| bool undo_ok = true; | ||
| if (undo_requires_target_context) { | ||
| CUcontext current = nullptr; | ||
| undo_ok = p_cuCtxGetCurrent(¤t) == CUDA_SUCCESS | ||
| && current == as_cu(h_context); | ||
| } | ||
| if (undo_ok) { | ||
| std::invoke(std::forward<Undo>(undo)); | ||
| } | ||
| status_ = p_cuCtxSetCurrent(target); | ||
| changed_ = status_ == CUDA_SUCCESS; | ||
| } | ||
| return composite; | ||
| } |
There was a problem hiding this comment.
I played a lot of defense here, and it is easy to see how complex this gets as errors cascade. I partly question the value of this kind of code. Would it be reasonable to call std::abort instead when a key invariant cannot be maintained? If context restoration fails, then whether or not the undo succeeds, the wrong context may remain current, and the program is in big trouble either way.
| event_registry.unregister_handle(b->resource); | ||
| GILReleaseGuard gil; | ||
| p_cuEventDestroy(b->resource); | ||
| pw_cuEventDestroy(b->resource); |
There was a problem hiding this comment.
Replaced deallocation functions (p_*) in destructors with wrapped versions that issue warnings on failure (pw_*).
Description
closes #2311
Devicemethods that create resources or synchronize (create_stream,create_event,create_opaque_array,create_mipmapped_array,create_texture_object,create_surface_object,sync) previously operated on whatever context was current on the calling thread, sodev1.create_stream()could silently create a stream on device 0 while labeling itdevice_id == 1.These methods now run against the
Device's bound context and restore the caller's current context afterward, including when no context is current. Details:Device.sync()synchronizes the bound context.Device.set_current()returns the previously current context._SynchronousMemoryResourcebinds its context at construction.Checklist
🤖 Generated with Claude Code