diff --git a/cuda_core/cuda/core/_cpp/rt/api.hpp b/cuda_core/cuda/core/_cpp/rt/api.hpp index dabaac2a5c8..4abf85bd5dc 100644 --- a/cuda_core/cuda/core/_cpp/rt/api.hpp +++ b/cuda_core/cuda/core/_cpp/rt/api.hpp @@ -243,7 +243,8 @@ StreamHandle deallocation_stream(const DevicePtrHandle &h) noexcept; // Set the deallocation stream for a device pointer handle. // Returns CUDA_ERROR_INVALID_CONTEXT when a default-stream token cannot be // bound because no CUDA context is current. -CUresult set_deallocation_stream(const DevicePtrHandle &h, const StreamHandle &h_stream) noexcept; +// May throw std::bad_alloc. +CUresult set_deallocation_stream(const DevicePtrHandle &h, const StreamHandle &h_stream); // ============================================================================ // Virtual memory management (VMM_DESIGN.md) diff --git a/cuda_core/cuda/core/_cpp/rt/internal.hpp b/cuda_core/cuda/core/_cpp/rt/internal.hpp index 382c4901f11..9b0ef9d63aa 100644 --- a/cuda_core/cuda/core/_cpp/rt/internal.hpp +++ b/cuda_core/cuda/core/_cpp/rt/internal.hpp @@ -60,8 +60,9 @@ struct DeallocationStream { // Implemented in stream.cpp ContextHandle deallocation_context(const DeallocationStream &stream) noexcept; -// Implemented in stream.cpp -bool make_deallocation_stream(const StreamHandle &h, DeallocationStream &out) noexcept; +// Implemented in stream.cpp. Not noexcept: binding a default-stream token +// allocates. May throw std::bad_alloc. +bool make_deallocation_stream(const StreamHandle &h, DeallocationStream &out); // Implemented in virtual_memory.cpp. Synchronize the stream a VMM buffer // recorded before its mappings are released. Skips the sync, with a report diff --git a/cuda_core/cuda/core/_cpp/rt/memory.cpp b/cuda_core/cuda/core/_cpp/rt/memory.cpp index f280aeedc5b..a80ffd3a2a1 100644 --- a/cuda_core/cuda/core/_cpp/rt/memory.cpp +++ b/cuda_core/cuda/core/_cpp/rt/memory.cpp @@ -193,7 +193,7 @@ deallocation_stream(const DevicePtrHandle &h) noexcept // Replace the stream that orders a device pointer's deallocation. CUresult -set_deallocation_stream(const DevicePtrHandle &h, const StreamHandle &h_stream) noexcept +set_deallocation_stream(const DevicePtrHandle &h, const StreamHandle &h_stream) { if (!h) { return CUDA_ERROR_INVALID_VALUE; diff --git a/cuda_core/cuda/core/_cpp/rt/stream.cpp b/cuda_core/cuda/core/_cpp/rt/stream.cpp index eba71ca5be8..917106a42c4 100644 --- a/cuda_core/cuda/core/_cpp/rt/stream.cpp +++ b/cuda_core/cuda/core/_cpp/rt/stream.cpp @@ -215,9 +215,11 @@ create_context_bound_legacy_stream(const ContextHandle &h_context) namespace detail { // Real streams are copied unchanged. Default-stream tokens without an embedded // context are bound to the current context. Returns false (and sets err) when a -// default-stream token cannot be bound because no context is current. +// default-stream token cannot be bound because no context is current. Binding +// a default-stream token allocates (the bound StreamBox and the current-context +// handle), so this may throw std::bad_alloc and must not be noexcept. bool -make_deallocation_stream(const StreamHandle &h, DeallocationStream &out) noexcept +make_deallocation_stream(const StreamHandle &h, DeallocationStream &out) { out = {}; if (!h) { diff --git a/cuda_core/cuda/core/_memory/_buffer.pyx b/cuda_core/cuda/core/_memory/_buffer.pyx index 23ab10c06e9..52fba667886 100644 --- a/cuda_core/cuda/core/_memory/_buffer.pyx +++ b/cuda_core/cuda/core/_memory/_buffer.pyx @@ -316,7 +316,13 @@ cdef class Buffer: record_stream = mr.is_device_accessible self._h_ptr = deviceptr_create_with_mr(c_ptr, size, mr) if record_stream: - _ds_status = set_deallocation_stream(self._h_ptr, s._h_stream) + try: + _ds_status = set_deallocation_stream(self._h_ptr, s._h_stream) + except MemoryError: + # Reset for the same reason as below, inside the handler so the + # destructor cannot swallow the MemoryError that is propagating. + self._h_ptr.reset() + raise if _ds_status != cydriver.CUresult.CUDA_SUCCESS: # Reset before raising: the DevicePtrHandle destructor would otherwise # invoke _mr_dealloc_callback, which catches any inner exception and diff --git a/cuda_core/cuda/core/_rt.pxd b/cuda_core/cuda/core/_rt.pxd index 4d5e63cb2bc..60617192c4b 100644 --- a/cuda_core/cuda/core/_rt.pxd +++ b/cuda_core/cuda/core/_rt.pxd @@ -295,7 +295,7 @@ cdef void register_mr_dealloc_callback(MRDeallocCallback cb) noexcept cdef DevicePtrHandle deviceptr_import_ipc( const MemoryPoolHandle& h_pool, const void* export_data, const StreamHandle& h_stream) except+ nogil cdef StreamHandle deallocation_stream(const DevicePtrHandle& h) noexcept nogil -cdef cydriver.CUresult set_deallocation_stream(const DevicePtrHandle& h, const StreamHandle& h_stream) noexcept nogil +cdef cydriver.CUresult set_deallocation_stream(const DevicePtrHandle& h, const StreamHandle& h_stream) except+ nogil # Virtual memory management (VMM_DESIGN.md) cdef MemAllocationHandle create_mem_allocation_handle( diff --git a/cuda_core/cuda/core/_rt.pyx b/cuda_core/cuda/core/_rt.pyx index 49523bbce91..ee7db861d6e 100644 --- a/cuda_core/cuda/core/_rt.pyx +++ b/cuda_core/cuda/core/_rt.pyx @@ -175,7 +175,7 @@ cdef extern from "_cpp/rt/rt.hpp" namespace "cuda_core::rt": StreamHandle deallocation_stream "cuda_core::rt::deallocation_stream" ( const DevicePtrHandle& h) noexcept nogil cydriver.CUresult set_deallocation_stream "cuda_core::rt::set_deallocation_stream" ( - const DevicePtrHandle& h, const StreamHandle& h_stream) noexcept nogil + const DevicePtrHandle& h, const StreamHandle& h_stream) except+ nogil # Virtual memory management (VMM_DESIGN.md) MemAllocationHandle create_mem_allocation_handle "cuda_core::rt::create_mem_allocation_handle" ( diff --git a/cuda_core/docs/source/release/1.3.0-notes.rst b/cuda_core/docs/source/release/1.3.0-notes.rst index 7a0d6cb0e01..e17192853d8 100644 --- a/cuda_core/docs/source/release/1.3.0-notes.rst +++ b/cuda_core/docs/source/release/1.3.0-notes.rst @@ -163,6 +163,10 @@ Fixes and enhancements - Fixed a ``TypeError`` in :meth:`Program.compile` when retrying after PCH heap exhaustion. +- :meth:`Buffer.set_deallocation_stream` and :meth:`Buffer.from_handle` now raise + :class:`MemoryError` when the host runs out of memory while binding a default + stream, instead of aborting the process with ``std::terminate``. + - :attr:`system.Device.uuid_without_prefix` preserves UUIDs that NVML already reports without a prefix, such as on Orin. This also allows :meth:`system.Device.to_cuda_device` to match those devices correctly.