Sarek_vulkan.Vulkan_api_releaseDeferred release of device buffers, and the allocation retry that depends on it. free runs from Gc.finalise finalisers (Gpu_memory.register_finalizer), which OCaml 5 runs at any allocation point of any domain, even inside batch_lock or queue_lock. A free therefore never submits, records, waits or consumes an error, and never blocks on the batch or queue lock: it queues its release (lock-free) and asks the stream layer to run the queue if the device is idle, which takes the batch lock with try_lock only. The stream registry snapshot it needs uses a short mutex never held across a blocking call; re-entry on it from a finaliser raises, is caught here, and leaves the release queued. A queue that stays non-empty is run at the next drain, idle Stream.synchronize, allocation failure, device destroy, or at the next launch once it is over its bounds (see Vulkan_api_batch.settle).
module Device = Vulkan_api_devicemodule BE = Sarek_backend_error.Backend_errorDevice buffers allocated and not yet released (test instrumentation: a buffer whose release failed silently stays counted).
Test hook: the next n allocation attempts fail as out-of-device-memory errors.
val with_release_retry : Device.t -> (unit -> 'a) -> 'aRun attempt; if it fails for lack of memory while releases are queued, complete the launches, run the releases (keeping other streams' errors) and try once more. A failing retry re-raises the original error. Allocation never runs in a finaliser, so taking the batch lock here is safe.
val free : Device.t -> bytes:int -> (unit -> unit) -> unitQueue release (bytes of device memory) until no launch recorded so far can use the resource, and run the queue now if the device is idle. A handle of a destroyed device only queues: the device took its resources with it.