@@ -78,10 +78,11 @@ __all__ = [
7878 " MemoryUSMShared" ,
7979 " MemoryUSMHost" ,
8080 " MemoryUSMDevice" ,
81+ " MemoryIPCDevice" ,
8182 " USMAllocationError" ,
82- " SyclIpcGetMemHandle " ,
83- " SyclIpcOpenMemHandle " ,
84- " SyclIpcCloseMemHandle " ,
83+ " SyclIPCGetMemHandle " ,
84+ " SyclIPCOpenMemHandle " ,
85+ " SyclIPCCloseMemHandle " ,
8586]
8687
8788include " _sycl_usm_array_interface_utils.pxi"
@@ -753,7 +754,6 @@ cdef class _Memory:
753754 return _out
754755
755756
756-
757757cdef class MemoryUSMShared(_Memory):
758758 """
759759 MemoryUSMShared(nbytes, alignment=0, queue=None)
@@ -901,6 +901,60 @@ cdef class MemoryUSMDevice(_Memory):
901901 )
902902
903903
904+ cdef class MemoryIPCDevice(MemoryUSMDevice):
905+ """
906+ Class representing emory object backed by an IPC-mapped USM device pointer.
907+
908+ This class is not intended to be instantiated directly.
909+ """
910+
911+ @property
912+ def is_closed (self ):
913+ """ Whether the IPC mapping has been closed."""
914+ return self ._memory_ptr is NULL
915+
916+ def __dealloc__ (self ):
917+ cdef DPCTLSyclContextRef ctx_ref
918+ if self ._memory_ptr is not NULL :
919+ ctx_ref = DPCTLQueue_GetContext(self .queue.get_queue_ref())
920+ DPCTLIPCMem_CloseHandle(self ._memory_ptr, ctx_ref)
921+ DPCTLContext_Delete(ctx_ref)
922+ self ._memory_ptr = NULL
923+
924+ @staticmethod
925+ cdef object create_ipc_from_usm_pointer_size_qref(
926+ DPCTLSyclUSMRef USMRef,
927+ Py_ssize_t nbytes,
928+ DPCTLSyclQueueRef QRef,
929+ ):
930+ """ Create a MemoryIPCDevice wrapping an IPC-mapped pointer."""
931+ cdef DPCTLSyclQueueRef QRef_copy = NULL
932+ cdef _Memory base
933+
934+ if nbytes <= 0 :
935+ raise ValueError (" Number of bytes must be positive" )
936+ if QRef is NULL :
937+ raise TypeError (" Argument DPCTLSyclQueueRef is NULL" )
938+
939+ base = _Memory.__new__ (_Memory)
940+ base._cinit_empty()
941+ base._memory_ptr = USMRef
942+ base.nbytes = nbytes
943+
944+ QRef_copy = DPCTLQueue_Copy(QRef)
945+ if QRef_copy is NULL :
946+ raise ValueError (" Referenced queue could not be copied." )
947+ try :
948+ base.queue = SyclQueue._create(QRef_copy)
949+ except dpctl.SyclQueueCreationError as sqce:
950+ raise ValueError (
951+ " SyclQueue object could not be created from "
952+ " copy of referenced queue"
953+ ) from sqce
954+
955+ return MemoryIPCDevice(< object > base)
956+
957+
904958def as_usm_memory (obj ):
905959 """
906960 as_usm_memory(obj)
@@ -972,7 +1026,6 @@ cdef api object Memory_Make(
9721026 )
9731027
9741028
975-
9761029# ─── IPC Memory Handle ────────────────────────────────────────────────
9771030
9781031cdef class IPCMemoryHandle:
@@ -1021,11 +1074,13 @@ cdef class IPCMemoryHandle:
10211074
10221075 cdef bytes _handle_bytes
10231076 cdef SyclContext _ctx
1077+ cdef void * _opaque_ptr
10241078 cdef bint _closed
10251079
10261080 def __cinit__ (self ):
10271081 self ._handle_bytes = None
10281082 self ._ctx = None
1083+ self ._opaque_ptr = NULL
10291084 self ._closed = False
10301085
10311086 def __init__ (self , _Memory usm_memory not None , SyclContext context = None ):
@@ -1042,6 +1097,12 @@ cdef class IPCMemoryHandle:
10421097 if ptr is NULL :
10431098 raise ValueError (" USM memory object has a null pointer" )
10441099
1100+ cdef void * src_opaque = usm_memory._opaque_ptr
1101+ if src_opaque is NULL :
1102+ raise ValueError (
1103+ " USM memory object does not own its allocation"
1104+ )
1105+
10451106 cdef SyclDevice dev = usm_memory.sycl_device
10461107 if not dev.has_aspect_ext_oneapi_ipc_memory:
10471108 raise RuntimeError (
@@ -1069,6 +1130,7 @@ cdef class IPCMemoryHandle:
10691130 finally :
10701131 DPCTLIPCMem_FreeHandleData(data_out)
10711132
1133+ self ._opaque_ptr = OpaqueSmartPtr_Copy(src_opaque)
10721134 self ._ctx = context
10731135 self ._closed = False
10741136
@@ -1143,67 +1205,60 @@ cdef class IPCMemoryHandle:
11431205 if mapped_ptr is NULL :
11441206 raise RuntimeError (" DPCTLIPCMem_OpenHandle failed" )
11451207
1146- # Build a SyclQueue for the device+context, then wrap the
1147- # mapped pointer. If anything fails, close the mapping.
11481208 cdef SyclQueue q
11491209 try :
11501210 q = dpctl.SyclQueue(context, device)
1151- # Wrap as MemoryUSMDevice, memory_owner=True skips the
1152- # OpaqueSmartPtr so __dealloc__ won't call sycl::free.
1153- mem = _Memory.create_from_usm_pointer_size_qref(
1154- mapped_ptr, nbytes, q.get_queue_ref(), memory_owner = True )
1211+ mem = MemoryIPCDevice.create_ipc_from_usm_pointer_size_qref(
1212+ mapped_ptr, nbytes, q.get_queue_ref())
11551213 except Exception :
11561214 DPCTLIPCMem_CloseHandle(mapped_ptr, ctx_ref)
11571215 raise
1216+
11581217 return mem
11591218
11601219 @staticmethod
1161- def close_mapping (_Memory usm_memory not None ,
1162- SyclContext context = None ):
1220+ def close_mapping (_Memory usm_memory not None ):
11631221 """ Explicitly close an IPC mapping.
11641222
11651223 After calling this, *usm_memory* is invalidated and must not be
1166- used again. Its destructor will not attempt to free the pointer.
1224+ used again.
11671225
11681226 Parameters
11691227 ----------
11701228 usm_memory : _Memory
11711229 The memory object returned by :meth:`open`.
1172- context : dpctl.SyclContext, optional
1173- Context used when opening. Defaults to the memory's queue
1174- context.
11751230 """
1231+ cdef DPCTLSyclUSMRef ptr
1232+ cdef DPCTLSyclContextRef ctx_ref
1233+
11761234 if not DPCTLIPCMem_Available():
11771235 raise RuntimeError (" IPC memory not supported in this build" )
11781236
1179- cdef DPCTLSyclUSMRef ptr = usm_memory._memory_ptr
1180- if ptr is NULL :
1181- return
1182-
1183- # Only IPC-mapped objects (no owning smart pointer) can be closed.
1184- if usm_memory._opaque_ptr is not NULL :
1237+ if not isinstance (usm_memory, MemoryIPCDevice):
11851238 raise RuntimeError (
1186- " close_mapping called on an owning USM allocation ; "
1239+ " close_mapping called on a non-IPC memory object ; "
11871240 " this method is only valid for IPC-mapped memory"
11881241 )
11891242
1190- if context is None :
1191- context = usm_memory.queue.sycl_context
1243+ ptr = usm_memory._memory_ptr
1244+ if ptr is NULL :
1245+ return
11921246
1193- cdef DPCTLSyclContextRef ctx_ref = context.get_context_ref( )
1247+ ctx_ref = DPCTLQueue_GetContext(usm_memory.queue.get_queue_ref() )
11941248 DPCTLIPCMem_CloseHandle(ptr, ctx_ref)
1249+ DPCTLContext_Delete(ctx_ref)
11951250
1196- # Fully invalidate so subsequent use is inert and __dealloc__
1197- # won't call sycl::free.
11981251 usm_memory._memory_ptr = NULL
1199- usm_memory._opaque_ptr = NULL
12001252 usm_memory.nbytes = 0
12011253
12021254 def close (self ):
12031255 """ Mark this handle object as closed."""
12041256 self ._closed = True
12051257
12061258 def __dealloc__ (self ):
1259+ if self ._opaque_ptr is not NULL :
1260+ OpaqueSmartPtr_Delete(self ._opaque_ptr)
1261+ self ._opaque_ptr = NULL
12071262 self ._closed = True
12081263
12091264 def __repr__ (self ):
@@ -1215,10 +1270,9 @@ cdef class IPCMemoryHandle:
12151270 )
12161271
12171272
1218-
12191273# ─── SYCL IPC free functions ──────────────────────────────────────────
12201274
1221- def SyclIpcGetMemHandle (_Memory usm_memory not None , SyclContext context = None ):
1275+ def SyclIPCGetMemHandle (_Memory usm_memory not None , SyclContext context = None ):
12221276 """ Export a USM device allocation as IPC handle bytes.
12231277
12241278 Parameters
@@ -1237,7 +1291,7 @@ def SyclIpcGetMemHandle(_Memory usm_memory not None, SyclContext context=None):
12371291 return h.to_bytes()
12381292
12391293
1240- def SyclIpcOpenMemHandle (bytes handle_bytes not None ,
1294+ def SyclIPCOpenMemHandle (bytes handle_bytes not None ,
12411295 SyclDevice device not None ,
12421296 Py_ssize_t nbytes ,
12431297 SyclContext context = None ):
@@ -1246,7 +1300,7 @@ def SyclIpcOpenMemHandle(bytes handle_bytes not None,
12461300 Parameters
12471301 ----------
12481302 handle_bytes : bytes
1249- Opaque payload from :func:`SyclIpcGetMemHandle `.
1303+ Opaque payload from :func:`SyclIPCGetMemHandle `.
12501304 device : dpctl.SyclDevice
12511305 Device to map the memory on.
12521306 nbytes : int
@@ -1257,22 +1311,19 @@ def SyclIpcOpenMemHandle(bytes handle_bytes not None,
12571311 Returns
12581312 -------
12591313 MemoryUSMDevice
1260- Mapped IPC memory. Must be closed with :func:`SyclIpcCloseMemHandle `.
1314+ Mapped IPC memory. Must be closed with :func:`SyclIPCCloseMemHandle `.
12611315 """
12621316 return IPCMemoryHandle.open(handle_bytes, device, nbytes, context)
12631317
12641318
1265- def SyclIpcCloseMemHandle (_Memory usm_memory not None ,
1266- SyclContext context = None ):
1319+ def SyclIPCCloseMemHandle (_Memory usm_memory not None ):
12671320 """ Close an IPC memory mapping.
12681321
12691322 After this call, *usm_memory* is invalidated.
12701323
12711324 Parameters
12721325 ----------
12731326 usm_memory : _Memory
1274- The memory object returned by :func:`SyclIpcOpenMemHandle`.
1275- context : dpctl.SyclContext, optional
1276- Context used when opening.
1327+ The memory object returned by :func:`SyclIPCOpenMemHandle`.
12771328 """
1278- IPCMemoryHandle.close_mapping(usm_memory, context )
1329+ IPCMemoryHandle.close_mapping(usm_memory)
0 commit comments