Skip to content

Commit 2e8eaf7

Browse files
committed
Use exactly-once VMM handle cleanup hooks
1 parent 76efd95 commit 2e8eaf7

3 files changed

Lines changed: 28 additions & 30 deletions

File tree

cuda_core/cuda/core/_memory/_virtual_memory_resource.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,7 @@ def _grow_allocation_fast_path(
322322
)
323323
res, new_handle = driver.cuMemCreate(aligned_additional_size, prop, 0)
324324
raise_if_driver_error(res)
325-
trans.on_failure(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
326-
trans.on_success(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
325+
trans.on_exit(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
327326

328327
# Map the new physical memory to the extended VA range
329328
(res,) = driver.cuMemMap(new_ptr, aligned_additional_size, 0, new_handle, 0)
@@ -389,8 +388,7 @@ def _grow_allocation_slow_path(
389388
# Get the old allocation handle for remapping
390389
result, old_handle = driver.cuMemRetainAllocationHandle(buf.handle)
391390
raise_if_driver_error(result)
392-
trans.on_failure(lambda h=old_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
393-
trans.on_success(lambda h=old_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
391+
trans.on_exit(lambda h=old_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
394392

395393
# Unmap the old VA range (aligned previous size)
396394
aligned_prev_size = total_aligned_size - aligned_additional_size
@@ -418,8 +416,7 @@ def _remap_old() -> None:
418416
# Create new physical memory for the additional size
419417
res, new_handle = driver.cuMemCreate(aligned_additional_size, prop, 0)
420418
raise_if_driver_error(res)
421-
trans.on_failure(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
422-
trans.on_success(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
419+
trans.on_exit(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
423420

424421
# Map the new physical memory to the extended portion (aligned offset)
425422
(res,) = driver.cuMemMap(int(new_ptr) + aligned_prev_size, aligned_additional_size, 0, new_handle, 0)
@@ -541,9 +538,8 @@ def allocate(self, size: int, *, stream: Stream | GraphBuilder | None = None) ->
541538
# ---- Create physical memory ----
542539
res, handle = driver.cuMemCreate(aligned_size, prop, 0)
543540
raise_if_driver_error(res)
544-
trans.on_failure(lambda h=handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
545541
# Once mapped, the physical allocation is kept alive without the creation reference.
546-
trans.on_success(lambda h=handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
542+
trans.on_exit(lambda h=handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
547543

548544
# ---- Reserve VA space ----
549545
# Potentially, use a separate size for the VA reservation from the physical allocation size

cuda_core/cuda/core/_utils/cuda_utils.pyi

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,22 @@ class NVRTCError(CUDAError):
1919

2020
class Transaction:
2121
"""
22-
A context manager for transactional operations with failure and success callbacks.
22+
A context manager for transactional operations with failure and exit callbacks.
2323
2424
Failure callbacks are executed in LIFO order if the transaction exits without being committed.
25-
Success callbacks are executed in FIFO order when the transaction is committed.
25+
Exit callbacks always run: in LIFO order on rollback or FIFO order during commit.
2626
2727
Usage:
2828
with Transaction() as txn:
2929
txn.on_failure(some_cleanup_function, arg1, arg2)
30-
txn.on_success(some_finalize_function, arg1, arg2)
30+
txn.on_exit(some_finalize_function, arg1, arg2)
3131
# ... perform operations ...
3232
txn.commit()
3333
3434
Methods:
3535
on_failure(fn, *args, **kwargs): Register a callback to be called on rollback.
36-
on_success(fn, *args, **kwargs): Register a callback to be called on commit.
37-
commit(): Disarm failure callbacks and run success callbacks.
36+
on_exit(fn, *args, **kwargs): Register a callback to be called on rollback or commit.
37+
commit(): Disarm failure callbacks and run exit callbacks.
3838
"""
3939

4040
def __init__(self) -> None:
@@ -52,15 +52,15 @@ class Transaction:
5252
Values are bound now via partial so late mutations don't bite you.
5353
"""
5454

55-
def on_success(self, fn: Callable[..., Any], /, *args: Any, **kwargs) -> None:
55+
def on_exit(self, fn: Callable[..., Any], /, *args: Any, **kwargs) -> None:
5656
"""
57-
Register a success callback (runs in FIFO order during commit()).
57+
Register an exit callback (runs exactly once, on rollback or during commit()).
5858
Values are bound now via partial so late mutations don't bite you.
5959
"""
6060

6161
def commit(self) -> None:
6262
"""
63-
Disarm all failure callbacks, then run success callbacks in FIFO order.
63+
Disarm all failure callbacks, then run exit callbacks in FIFO order.
6464
"""
6565
_keep_driver_in_stub: 'driver.CUresult'
6666
_keep_nvrtc_in_stub: 'nvrtc.nvrtcResult'

cuda_core/cuda/core/_utils/cuda_utils.pyx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -306,26 +306,26 @@ def is_nested_sequence(obj: object) -> bool:
306306

307307
class Transaction:
308308
"""
309-
A context manager for transactional operations with failure and success callbacks.
309+
A context manager for transactional operations with failure and exit callbacks.
310310

311311
Failure callbacks are executed in LIFO order if the transaction exits without being committed.
312-
Success callbacks are executed in FIFO order when the transaction is committed.
312+
Exit callbacks always run: in LIFO order on rollback or FIFO order during commit.
313313

314314
Usage:
315315
with Transaction() as txn:
316316
txn.on_failure(some_cleanup_function, arg1, arg2)
317-
txn.on_success(some_finalize_function, arg1, arg2)
317+
txn.on_exit(some_finalize_function, arg1, arg2)
318318
# ... perform operations ...
319319
txn.commit()
320320

321321
Methods:
322322
on_failure(fn, *args, **kwargs): Register a callback to be called on rollback.
323-
on_success(fn, *args, **kwargs): Register a callback to be called on commit.
324-
commit(): Disarm failure callbacks and run success callbacks.
323+
on_exit(fn, *args, **kwargs): Register a callback to be called on rollback or commit.
324+
commit(): Disarm failure callbacks and run exit callbacks.
325325
"""
326326
def __init__(self) -> None:
327327
self._stack = ExitStack()
328-
self._on_success: list[Callable[[], Any]] = []
328+
self._on_exit: list[Callable[[], Any]] = []
329329
self._entered = False
330330
331331
def __enter__(self):
@@ -336,7 +336,7 @@ class Transaction:
336336
def __exit__(self, exc_type, exc, tb):
337337
# If exit callbacks remain, they'll run in LIFO order.
338338
self._entered = False
339-
self._on_success.clear()
339+
self._on_exit.clear()
340340
return self._stack.__exit__(exc_type, exc, tb)
341341
342342
def on_failure(self, fn: Callable[..., Any], /, *args: Any, **kwargs: Any) -> None:
@@ -348,24 +348,26 @@ class Transaction:
348348
raise RuntimeError("Transaction must be entered before on_failure()")
349349
self._stack.callback(partial(fn, *args, **kwargs))
350350
351-
def on_success(self, fn: Callable[..., Any], /, *args: Any, **kwargs: Any) -> None:
351+
def on_exit(self, fn: Callable[..., Any], /, *args: Any, **kwargs: Any) -> None:
352352
"""
353-
Register a success callback (runs in FIFO order during commit()).
353+
Register an exit callback (runs exactly once, on rollback or during commit()).
354354
Values are bound now via partial so late mutations don't bite you.
355355
"""
356356
if not self._entered:
357-
raise RuntimeError("Transaction must be entered before on_success()")
358-
self._on_success.append(partial(fn, *args, **kwargs))
357+
raise RuntimeError("Transaction must be entered before on_exit()")
358+
callback = partial(fn, *args, **kwargs)
359+
self._stack.callback(callback)
360+
self._on_exit.append(callback)
359361
360362
def commit(self) -> None:
361363
"""
362-
Disarm all failure callbacks, then run success callbacks in FIFO order.
364+
Disarm all failure callbacks, then run exit callbacks in FIFO order.
363365
"""
364366
# pop_all() empties this stack so no callbacks are triggered on exit.
365367
self._stack.pop_all()
366-
for fn in self._on_success:
368+
for fn in self._on_exit:
367369
fn()
368-
self._on_success.clear()
370+
self._on_exit.clear()
369371
370372
371373
# Track whether we've already warned about fork method

0 commit comments

Comments
 (0)