@@ -902,6 +902,16 @@ cdef class MemoryUSMDevice(_Memory):
902902 )
903903
904904
905+ cdef void _close_ipc_ptr(DPCTLSyclUSMRef ptr, DPCTLSyclQueueRef qref) noexcept:
906+ """ Close an IPC-mapped pointer using the context derived from *qref*."""
907+ cdef DPCTLSyclContextRef ctx_ref
908+ if ptr is NULL :
909+ return
910+ ctx_ref = DPCTLQueue_GetContext(qref)
911+ DPCTLIPCMem_CloseHandle(ptr, ctx_ref)
912+ DPCTLContext_Delete(ctx_ref)
913+
914+
905915cdef class MemoryIPCDevice(MemoryUSMDevice):
906916 """
907917 Class representing memory object backed by an IPC-mapped USM device pointer.
@@ -915,11 +925,8 @@ cdef class MemoryIPCDevice(MemoryUSMDevice):
915925 return self ._memory_ptr is NULL
916926
917927 def __dealloc__ (self ):
918- cdef DPCTLSyclContextRef ctx_ref
919928 if self ._memory_ptr is not NULL :
920- ctx_ref = DPCTLQueue_GetContext(self .queue.get_queue_ref())
921- DPCTLIPCMem_CloseHandle(self ._memory_ptr, ctx_ref)
922- DPCTLContext_Delete(ctx_ref)
929+ _close_ipc_ptr(self ._memory_ptr, self .queue.get_queue_ref())
923930 self ._memory_ptr = NULL
924931
925932 @staticmethod
@@ -928,14 +935,19 @@ cdef class MemoryIPCDevice(MemoryUSMDevice):
928935 Py_ssize_t nbytes,
929936 DPCTLSyclQueueRef QRef,
930937 ):
931- """ Create a MemoryIPCDevice wrapping an IPC-mapped pointer."""
938+ """ Create a MemoryIPCDevice wrapping an IPC-mapped pointer.
939+
940+ Takes **sole ownership** of *USMRef*. On any failure the mapping
941+ is closed exactly once before the exception propagates.
942+ """
932943 cdef DPCTLSyclQueueRef QRef_copy = NULL
933944 cdef _Memory base
934945
935- if nbytes <= 0 :
936- raise ValueError (" Number of bytes must be positive" )
937946 if QRef is NULL :
938947 raise TypeError (" Argument DPCTLSyclQueueRef is NULL" )
948+ if nbytes <= 0 :
949+ _close_ipc_ptr(USMRef, QRef)
950+ raise ValueError (" Number of bytes must be positive" )
939951
940952 base = _Memory.__new__ (_Memory)
941953 base._cinit_empty()
@@ -944,11 +956,14 @@ cdef class MemoryIPCDevice(MemoryUSMDevice):
944956
945957 QRef_copy = DPCTLQueue_Copy(QRef)
946958 if QRef_copy is NULL :
959+ _close_ipc_ptr(USMRef, QRef)
960+ base._memory_ptr = NULL
947961 raise ValueError (" Referenced queue could not be copied." )
948962 try :
949963 base.queue = SyclQueue._create(QRef_copy)
950964 except dpctl.SyclQueueCreationError as sqce:
951- base._memory_ptr = NULL # caller retains ownership of USMRef
965+ _close_ipc_ptr(USMRef, QRef)
966+ base._memory_ptr = NULL
952967 raise ValueError (
953968 " SyclQueue object could not be created from "
954969 " copy of referenced queue"
@@ -957,9 +972,8 @@ cdef class MemoryIPCDevice(MemoryUSMDevice):
957972 try :
958973 result = MemoryIPCDevice(< object > base)
959974 except Exception :
960- # If MemoryIPCDevice.__cinit__ failed after _cinit_other copied
961- # _memory_ptr, the partial object's __dealloc__ already closed
962- # the IPC handle. Null base to prevent any further action.
975+ # MemoryIPCDevice.__dealloc__ already closed the IPC handle.
976+ # Null base to prevent base's dealloc from acting on it.
963977 base._memory_ptr = NULL
964978 raise
965979 base._memory_ptr = NULL # ownership fully transferred to result
@@ -1226,21 +1240,11 @@ cdef class IPCMemoryHandle:
12261240 DPCTLIPCMem_CloseHandle(mapped_ptr, ctx_ref)
12271241 raise
12281242
1229- try :
1230- mem = MemoryIPCDevice.create_ipc_from_usm_pointer_size_qref(
1231- mapped_ptr, nbytes, q.get_queue_ref())
1232- except ValueError :
1233- # Failed before MemoryIPCDevice construction (queue copy, etc).
1234- # Handle was NOT closed — we still own it.
1235- DPCTLIPCMem_CloseHandle(mapped_ptr, ctx_ref)
1236- raise
1237- except Exception :
1238- # Failed during MemoryIPCDevice construction.
1239- # MemoryIPCDevice.__dealloc__ already closed the IPC handle.
1240- # Do NOT close again to avoid double-close (undefined behavior).
1241- raise
1242-
1243- return mem
1243+ # Ownership of mapped_ptr transfers unconditionally to the helper.
1244+ # It will close the mapping on any failure; on success the returned
1245+ # MemoryIPCDevice owns it.
1246+ return MemoryIPCDevice.create_ipc_from_usm_pointer_size_qref(
1247+ mapped_ptr, nbytes, q.get_queue_ref())
12441248
12451249 @staticmethod
12461250 def close_mapping (MemoryUSMDevice usm_memory not None ):
0 commit comments