@@ -322,8 +322,17 @@ def _grow_allocation_fast_path(
322322 )
323323 res , new_handle = driver .cuMemCreate (aligned_additional_size , prop , 0 )
324324 raise_if_driver_error (res )
325- # Register undo for creation
326- trans .append (lambda h = new_handle : raise_if_driver_error (driver .cuMemRelease (h )[0 ]))
325+ new_handle_released = False
326+
327+ def _release_new_handle () -> None :
328+ nonlocal new_handle_released
329+ if not new_handle_released :
330+ raise_if_driver_error (driver .cuMemRelease (new_handle )[0 ])
331+ new_handle_released = True
332+
333+ # Register undo for creation. Callback is conditional to avoid
334+ # double-release after an explicit successful release.
335+ trans .append (_release_new_handle )
327336
328337 # Map the new physical memory to the extended VA range
329338 (res ,) = driver .cuMemMap (new_ptr , aligned_additional_size , 0 , new_handle , 0 )
@@ -339,6 +348,9 @@ def _grow_allocation_fast_path(
339348 (res ,) = driver .cuMemSetAccess (new_ptr , aligned_additional_size , descs , len (descs ))
340349 raise_if_driver_error (res )
341350
351+ # Release handle ownership now that mapping is stable.
352+ _release_new_handle ()
353+
342354 # All succeeded, cancel undo actions
343355 trans .commit ()
344356
@@ -389,8 +401,17 @@ def _grow_allocation_slow_path(
389401 # Get the old allocation handle for remapping
390402 result , old_handle = driver .cuMemRetainAllocationHandle (buf .handle )
391403 raise_if_driver_error (result )
392- # Register undo for old_handle
393- trans .append (lambda h = old_handle : raise_if_driver_error (driver .cuMemRelease (h )[0 ]))
404+ old_handle_released = False
405+
406+ def _release_old_handle () -> None :
407+ nonlocal old_handle_released
408+ if not old_handle_released :
409+ raise_if_driver_error (driver .cuMemRelease (old_handle )[0 ])
410+ old_handle_released = True
411+
412+ # Register undo for old handle. Callback is conditional to avoid
413+ # double-release after explicit success.
414+ trans .append (_release_old_handle )
394415
395416 # Unmap the old VA range (aligned previous size)
396417 aligned_prev_size = total_aligned_size - aligned_additional_size
@@ -419,8 +440,17 @@ def _remap_old() -> None:
419440 res , new_handle = driver .cuMemCreate (aligned_additional_size , prop , 0 )
420441 raise_if_driver_error (res )
421442
422- # Register undo for new physical memory
423- trans .append (lambda h = new_handle : raise_if_driver_error (driver .cuMemRelease (h )[0 ]))
443+ new_handle_released = False
444+
445+ def _release_new_handle () -> None :
446+ nonlocal new_handle_released
447+ if not new_handle_released :
448+ raise_if_driver_error (driver .cuMemRelease (new_handle )[0 ])
449+ new_handle_released = True
450+
451+ # Register undo for new physical memory. Callback is conditional to
452+ # avoid double-release after explicit success.
453+ trans .append (_release_new_handle )
424454
425455 # Map the new physical memory to the extended portion (aligned offset)
426456 (res ,) = driver .cuMemMap (int (new_ptr ) + aligned_prev_size , aligned_additional_size , 0 , new_handle , 0 )
@@ -439,6 +469,10 @@ def _remap_old() -> None:
439469 (res ,) = driver .cuMemSetAccess (new_ptr , total_aligned_size , descs , len (descs ))
440470 raise_if_driver_error (res )
441471
472+ # Release handles once all operations that need them have completed.
473+ _release_new_handle ()
474+ _release_old_handle ()
475+
442476 # All succeeded, cancel undo actions
443477 trans .commit ()
444478
@@ -542,8 +576,17 @@ def allocate(self, size: int, *, stream: Stream | GraphBuilder | None = None) ->
542576 # ---- Create physical memory ----
543577 res , handle = driver .cuMemCreate (aligned_size , prop , 0 )
544578 raise_if_driver_error (res )
545- # Register undo for physical memory
546- trans .append (lambda h = handle : raise_if_driver_error (driver .cuMemRelease (h )[0 ]))
579+ handle_released = False
580+
581+ def _release_handle () -> None :
582+ nonlocal handle_released
583+ if not handle_released :
584+ raise_if_driver_error (driver .cuMemRelease (handle )[0 ])
585+ handle_released = True
586+
587+ # Register undo for physical memory. Callback is conditional to
588+ # avoid double-release after explicit success.
589+ trans .append (_release_handle )
547590
548591 # ---- Reserve VA space ----
549592 # Potentially, use a separate size for the VA reservation from the physical allocation size
@@ -563,6 +606,9 @@ def allocate(self, size: int, *, stream: Stream | GraphBuilder | None = None) ->
563606 (res ,) = driver .cuMemSetAccess (ptr , aligned_size , descs , len (descs ))
564607 raise_if_driver_error (res )
565608
609+ # Release handle ownership once map+access setup succeeded.
610+ _release_handle ()
611+
566612 trans .commit ()
567613
568614 # Done — return a Buffer that tracks this VA range
0 commit comments