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:
@@ -592,6 +592,21 @@ pyrowave_result pyrowave_image_create(const pyrowave_image_create_info *info, py
|
||||
if (!img)
|
||||
return PYROWAVE_ERROR_FAILED_EXTERNAL_HANDLE;
|
||||
|
||||
// PUNKTFUNK (patch 0006): consume the imported by-reference NT handle exactly here, at the
|
||||
// API's success boundary — the allocator no longer closes it (see memory_allocator.cpp).
|
||||
// This pins pyrowave.h's documented contract ("take ownership and close the HANDLE on
|
||||
// import") to mean ON SUCCESS, matching pyrowave_sync_object_create's semantics: on ANY
|
||||
// failure return the caller still owns the handle and can close it unconditionally.
|
||||
// By-reference NT-handle imports never transfer ownership at the Vulkan level (the
|
||||
// implementation keeps its own reference), so a post-create close here is always legal.
|
||||
#ifdef _WIN32
|
||||
if (info->external_handle &&
|
||||
ExternalHandle::memory_handle_type_imports_by_reference(info->handle_type))
|
||||
{
|
||||
::CloseHandle(reinterpret_cast<HANDLE>(info->external_handle));
|
||||
}
|
||||
#endif
|
||||
|
||||
auto *image = new pyrowave_image_opaque();
|
||||
image->device = &device;
|
||||
image->img = std::move(img);
|
||||
|
||||
Reference in New Issue
Block a user