Skip to content

Commit 3c22874

Browse files
authored
Use C++ shared_ptr for IPC file descriptor cleanup (#1832)
* Use C++ shared_ptr for IPC file descriptor cleanup Replace Python-level os.close() in IPCAllocationHandle with a C++ shared_ptr custom deleter that calls POSIX close() directly, avoiding unraisable exception errors during late interpreter shutdown. Made-with: Cursor * Fix Windows build: use _close() on Win32 for fd cleanup Made-with: Cursor * Windows: no-op deleter for fd handle (IPC is Linux-only) Made-with: Cursor * Fix Windows build: no-op fd handle with runtime_error guard create_fd_handle and create_fd_handle_ref throw std::runtime_error on Windows since POSIX file descriptors are Linux-only. Made-with: Cursor
1 parent 545ead4 commit 3c22874

File tree

6 files changed

+72
-14
lines changed

6 files changed

+72
-14
lines changed

cuda_core/cuda/core/_cpp/resource_handles.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
#include <cstdint>
1010
#include <cstring>
1111
#include <mutex>
12+
#include <stdexcept>
1213
#include <unordered_map>
1314
#include <vector>
1415

16+
#ifndef _WIN32
17+
#include <unistd.h>
18+
#endif
19+
1520
namespace cuda_core {
1621

1722
// ============================================================================
@@ -1116,4 +1121,27 @@ CuLinkHandle create_culink_handle_ref(CUlinkState state) {
11161121
return CuLinkHandle(box, &box->resource);
11171122
}
11181123

1124+
// ============================================================================
1125+
// File Descriptor Handles
1126+
// ============================================================================
1127+
1128+
FileDescriptorHandle create_fd_handle(int fd) {
1129+
#ifdef _WIN32
1130+
throw std::runtime_error("create_fd_handle is not supported on Windows");
1131+
#else
1132+
return FileDescriptorHandle(
1133+
new int(fd),
1134+
[](const int* p) { ::close(*p); delete p; }
1135+
);
1136+
#endif
1137+
}
1138+
1139+
FileDescriptorHandle create_fd_handle_ref(int fd) {
1140+
#ifdef _WIN32
1141+
throw std::runtime_error("create_fd_handle_ref is not supported on Windows");
1142+
#else
1143+
return std::make_shared<const int>(fd);
1144+
#endif
1145+
}
1146+
11191147
} // namespace cuda_core

cuda_core/cuda/core/_cpp/resource_handles.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ using NvrtcProgramHandle = std::shared_ptr<const nvrtcProgram>;
154154
using NvvmProgramHandle = std::shared_ptr<const NvvmProgramValue>;
155155
using NvJitLinkHandle = std::shared_ptr<const NvJitLinkValue>;
156156
using CuLinkHandle = std::shared_ptr<const CUlinkState>;
157+
using FileDescriptorHandle = std::shared_ptr<const int>;
157158

158159

159160
// ============================================================================
@@ -477,6 +478,17 @@ CuLinkHandle create_culink_handle(CUlinkState state);
477478
// The handle will NOT be destroyed when the last reference is released.
478479
CuLinkHandle create_culink_handle_ref(CUlinkState state);
479480

481+
// ============================================================================
482+
// File descriptor handle functions
483+
// ============================================================================
484+
485+
// Create an owning file descriptor handle.
486+
// When the last reference is released, POSIX close() is called.
487+
FileDescriptorHandle create_fd_handle(int fd);
488+
489+
// Create a non-owning file descriptor handle (caller manages the fd).
490+
FileDescriptorHandle create_fd_handle_ref(int fd);
491+
480492
// ============================================================================
481493
// Overloaded helper functions to extract raw resources from handles
482494
// ============================================================================
@@ -596,6 +608,10 @@ inline std::intptr_t as_intptr(const CuLinkHandle& h) noexcept {
596608
return reinterpret_cast<std::intptr_t>(as_cu(h));
597609
}
598610

611+
inline std::intptr_t as_intptr(const FileDescriptorHandle& h) noexcept {
612+
return h ? static_cast<std::intptr_t>(*h) : -1;
613+
}
614+
599615
// as_py() - convert handle to Python wrapper object (returns new reference)
600616
#if PY_VERSION_HEX < 0x030D0000
601617
extern "C" int _Py_IsFinalizing(void);
@@ -687,4 +703,8 @@ inline PyObject* as_py(const GraphicsResourceHandle& h) noexcept {
687703
return detail::make_py("cuda.bindings.driver", "CUgraphicsResource", as_intptr(h));
688704
}
689705

706+
inline PyObject* as_py(const FileDescriptorHandle& h) noexcept {
707+
return PyLong_FromSsize_t(as_intptr(h));
708+
}
709+
690710
} // namespace cuda_core

cuda_core/cuda/core/_memory/_ipc.pxd

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from cuda.bindings cimport cydriver
66
from cuda.core._memory._buffer cimport Buffer
77
from cuda.core._memory._memory_pool cimport _MemPool
8+
from cuda.core._resource_handles cimport FileDescriptorHandle
89

910

1011
# Holds _MemPool objects imported by this process. This enables
@@ -46,8 +47,8 @@ cdef class IPCBufferDescriptor:
4647

4748
cdef class IPCAllocationHandle:
4849
cdef:
49-
int _handle
50-
object _uuid
50+
FileDescriptorHandle _h_fd
51+
object _uuid
5152

5253
cpdef close(self)
5354

cuda_core/cuda/core/_memory/_ipc.pyx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ from cuda.core._memory._memory_pool cimport _MemPool
1010
from cuda.core._stream cimport Stream
1111
from cuda.core._resource_handles cimport (
1212
DevicePtrHandle,
13+
create_fd_handle,
1314
create_mempool_handle_ipc,
1415
deviceptr_import_ipc,
1516
get_last_error,
1617
as_cu,
18+
as_intptr,
19+
as_py,
1720
)
1821

1922
from cuda.core._stream cimport default_stream
@@ -110,31 +113,24 @@ cdef class IPCAllocationHandle:
110113
def _init(cls, handle: int, uuid): # no-cython-lint
111114
cdef IPCAllocationHandle self = IPCAllocationHandle.__new__(cls)
112115
assert handle >= 0
113-
self._handle = handle
116+
self._h_fd = create_fd_handle(handle)
114117
self._uuid = uuid
115118
return self
116119

117120
cpdef close(self):
118121
"""Close the handle."""
119-
if self._handle >= 0:
120-
try:
121-
os.close(self._handle)
122-
finally:
123-
self._handle = -1
124-
125-
def __dealloc__(self):
126-
self.close()
122+
self._h_fd.reset()
127123

128124
def __int__(self) -> int:
129-
if self._handle < 0:
125+
if not self._h_fd or as_intptr(self._h_fd) < 0:
130126
raise ValueError(
131127
f"Cannot convert IPCAllocationHandle to int: the handle (id={id(self)}) is closed."
132128
)
133-
return self._handle
129+
return as_py(self._h_fd)
134130

135131
@property
136132
def handle(self) -> int:
137-
return self._handle
133+
return as_py(self._h_fd)
138134

139135
@property
140136
def uuid(self) -> uuid.UUID:

cuda_core/cuda/core/_resource_handles.pxd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ cdef extern from "_cpp/resource_handles.hpp" namespace "cuda_core":
4141
ctypedef shared_ptr[const NvJitLinkValue] NvJitLinkHandle
4242

4343
ctypedef shared_ptr[const cydriver.CUlinkState] CuLinkHandle
44+
ctypedef shared_ptr[const int] FileDescriptorHandle
4445

4546
# as_cu() - extract the raw CUDA handle (inline C++)
4647
cydriver.CUcontext as_cu(ContextHandle h) noexcept nogil
@@ -73,6 +74,7 @@ cdef extern from "_cpp/resource_handles.hpp" namespace "cuda_core":
7374
intptr_t as_intptr(NvvmProgramHandle h) noexcept nogil
7475
intptr_t as_intptr(NvJitLinkHandle h) noexcept nogil
7576
intptr_t as_intptr(CuLinkHandle h) noexcept nogil
77+
intptr_t as_intptr(FileDescriptorHandle h) noexcept nogil
7678

7779
# as_py() - convert handle to Python wrapper object (inline C++; requires GIL)
7880
object as_py(ContextHandle h)
@@ -89,6 +91,7 @@ cdef extern from "_cpp/resource_handles.hpp" namespace "cuda_core":
8991
object as_py(NvvmProgramHandle h)
9092
object as_py(NvJitLinkHandle h)
9193
object as_py(CuLinkHandle h)
94+
object as_py(FileDescriptorHandle h)
9295

9396

9497
# =============================================================================
@@ -203,3 +206,7 @@ cdef NvJitLinkHandle create_nvjitlink_handle_ref(cynvjitlink.nvJitLinkHandle han
203206
# cuLink handles
204207
cdef CuLinkHandle create_culink_handle(cydriver.CUlinkState state) except+ nogil
205208
cdef CuLinkHandle create_culink_handle_ref(cydriver.CUlinkState state) except+ nogil
209+
210+
# File descriptor handles
211+
cdef FileDescriptorHandle create_fd_handle(int fd) except+ nogil
212+
cdef FileDescriptorHandle create_fd_handle_ref(int fd) except+ nogil

cuda_core/cuda/core/_resource_handles.pyx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ cdef extern from "_cpp/resource_handles.hpp" namespace "cuda_core":
191191
CuLinkHandle create_culink_handle_ref "cuda_core::create_culink_handle_ref" (
192192
cydriver.CUlinkState state) except+ nogil
193193

194+
# File descriptor handles
195+
FileDescriptorHandle create_fd_handle "cuda_core::create_fd_handle" (
196+
int fd) except+ nogil
197+
FileDescriptorHandle create_fd_handle_ref "cuda_core::create_fd_handle_ref" (
198+
int fd) except+ nogil
199+
194200

195201
# =============================================================================
196202
# CUDA Driver API capsule

0 commit comments

Comments
 (0)