fix(encode/pyrowave): pin the NT-handle import contract to consume-on-success-only
import_plane closed the shared NT handle on every pyrowave_image_create
failure, believing pyrowave only consumes it on success. The vendored
truth was messier: Granite's allocator closed by-reference handles
UNCONDITIONALLY after the first vkAllocateMemory — success AND failure —
while failures before the allocator (validation, vkCreateImage, no
memory type) left the handle open, and both classes surface as the same
error code. So the call site could not know whether to close, and an
allocate-stage failure double-closed a possibly-recycled handle value
(audit WP5.3). The filed fix (DuplicateHandle + close the original
unconditionally) traded the double close for a guaranteed leak on every
pre-allocate failure and left the ambiguity in place.
Patch 0006 fixes the callee instead: the allocator never closes a
caller's handle, and pyrowave_image_create consumes it exactly at its
success return — which is what pyrowave.h ("take ownership and close the
HANDLE on import") documents, and what Granite's semaphore import
already did, so import_fence was correct as written and the two paths
now share one contract. The close-on-failure in import_plane is thereby
correct on EVERY path, including a vkBindImageMemory failure after a
successful allocate. Bonus fix recorded in the patch: the allocator's
block-recycling retry loop used to re-run vkAllocateMemory with
import_info still pointing at the just-closed handle.
Both-platform gates green; pyrowave_win_smoke (.173, 34/34) re-validates
the live import path against the rebuilt vendored C++.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -732,16 +732,15 @@ bool DeviceAllocator::internal_allocate(
|
||||
res = table->vkAllocateMemory(device->get_device(), &info, nullptr, &device_memory);
|
||||
}
|
||||
|
||||
// If we're importing, make sure we consume the native handle.
|
||||
if (external && bool(*external) &&
|
||||
ExternalHandle::memory_handle_type_imports_by_reference(external->memory_handle_type))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(external->handle);
|
||||
#else
|
||||
::close(external->handle);
|
||||
#endif
|
||||
}
|
||||
// PUNKTFUNK (patch 0006): the consume of by-reference native handles used to happen HERE,
|
||||
// unconditionally — success AND failure of the first vkAllocateMemory. That made the caller's
|
||||
// failure contract ambiguous (an allocate-stage failure had already closed the handle while a
|
||||
// create/find_memory_type failure had not), and the block-recycling retry loop below re-ran
|
||||
// vkAllocateMemory with import_info still pointing at the just-closed handle. The consume now
|
||||
// lives at the API commit point (pyrowave_c.cpp: pyrowave_image_create's success return), so
|
||||
// the allocator never closes a caller's handle and retries import a still-open handle.
|
||||
// (fd-type imports were never affected: memory_handle_type_imports_by_reference excludes
|
||||
// OPAQUE_FD/DMA_BUF, whose ownership vkAllocateMemory itself transfers on success.)
|
||||
|
||||
if (res == VK_SUCCESS)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user