Skip to content

Commit 0a67e67

Browse files
committed
add MemoryIPCDevice
1 parent 7f907d0 commit 0a67e67

4 files changed

Lines changed: 366 additions & 47 deletions

File tree

dpctl/memory/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,25 @@
3030
"""
3131

3232
from ._memory import (
33+
MemoryIPCDevice,
3334
MemoryUSMDevice,
3435
MemoryUSMHost,
3536
MemoryUSMShared,
37+
SyclIPCCloseMemHandle,
38+
SyclIPCGetMemHandle,
39+
SyclIPCOpenMemHandle,
3640
USMAllocationError,
3741
as_usm_memory,
38-
SyclIpcGetMemHandle,
39-
SyclIpcOpenMemHandle,
40-
SyclIpcCloseMemHandle,
4142
)
4243

4344
__all__ = [
45+
"MemoryIPCDevice",
4446
"MemoryUSMDevice",
4547
"MemoryUSMHost",
4648
"MemoryUSMShared",
4749
"USMAllocationError",
4850
"as_usm_memory",
49-
"SyclIpcGetMemHandle",
50-
"SyclIpcOpenMemHandle",
51-
"SyclIpcCloseMemHandle",
51+
"SyclIPCGetMemHandle",
52+
"SyclIPCOpenMemHandle",
53+
"SyclIPCCloseMemHandle",
5254
]

dpctl/memory/_memory.pxd

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,12 @@ cdef public api class MemoryUSMDevice(_Memory) [
9090
object PyMemoryUSMDeviceObject, type PyMemoryUSMDeviceType
9191
]:
9292
pass
93+
94+
95+
cdef class MemoryIPCDevice(MemoryUSMDevice):
96+
@staticmethod
97+
cdef object create_ipc_from_usm_pointer_size_qref(
98+
DPCTLSyclUSMRef USMRef,
99+
Py_ssize_t nbytes,
100+
DPCTLSyclQueueRef QRef,
101+
)

dpctl/memory/_memory.pyx

Lines changed: 92 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -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

8788
include "_sycl_usm_array_interface_utils.pxi"
@@ -753,7 +754,6 @@ cdef class _Memory:
753754
return _out
754755

755756

756-
757757
cdef 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+
904958
def 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

9781031
cdef 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

Comments
 (0)