Summary
Add a consistent closed-state contract to cuda.core resources. Closed objects must fail before a NULL native handle reaches CUDA.
Problem
Several cuda.core objects reset a shared native handle in close(), but their methods do not check that state. This can cause late CUDA errors, stale metadata, or invalid native access. Streams are the highest risk because CUDA can interpret a NULL CUstream as the default stream. Work that targets a closed stream can therefore run on the wrong stream instead of failing.
The same risk exists in event operations, buffer operations, memory pools, graph resources, compiler resources, texture resources, and cross-object conversion paths.
Required behavior
- Add
__bool__ where an object has a clear open or closed state.
- Test the shared handle state, not the numeric CUDA handle. An open
Stream.from_handle(0) and an open zero-size Buffer must remain true.
- Raise
RuntimeError("<Type> has been closed") before an active operation uses a closed resource.
- Keep repeated
close(), handle inspection, and repr() safe.
- Keep current equality and hash behavior. A separate change can address mutable hash or closed-object equality rules.
- Make
Stream.close() a no-op for CUDA default-stream tokens. These tokens use process-wide singleton wrappers and own no CUDA resource.
- Keep memory-pool
deallocate() available after pool close so that outstanding buffers can complete cleanup.
Scope
Apply the contract to these resources and their acceptance paths:
Stream, Event, Buffer, and ManagedBuffer.
Context, Program, and Linker.
DeviceMemoryResource, PinnedMemoryResource, and ManagedMemoryResource.
GraphBuilder, executable Graph, destroyed GraphNode objects, and invalid child GraphDefinition views.
OpaqueArray, MipmappedArray, TextureObject, SurfaceObject, and GraphicsResource.
IPCAllocationHandle.
Update central conversion and acceptance paths. This includes Stream_accept, stream protocol conversion, event use in streams and graphs, buffer use in copy and managed-memory operations, DLPack, IPC, memory views, kernel arguments, virtual memory, and texture descriptors.
A valid virtual graph entry node and a destroyed graph node both have a NULL CUgraphNode. Add explicit state that distinguishes them. Reject destroyed nodes in graph construction and adjacency changes. Reject invalid child graph views in graph queries, embedding, instantiation, and updates.
GraphMemoryResource.close() and ProgramCacheResource.close() are out of scope because they do not create a closed native-resource state.
Buffer deallocation stream
Buffer.set_deallocation_stream() and Buffer.close(stream=...) must reject a closed Stream or closed GraphBuilder. A failed update must leave the current deallocation stream unchanged. A closed Buffer must remain rejected as specified in #2600 and #2602.
Acceptance criteria
- Each covered resource reports the correct Boolean state before and after close or invalidation.
- No covered active operation passes a released handle to CUDA.
Stream_accept rejects every closed stream source.
- Closed events are rejected by stream and graph event operations.
- Closed buffers are rejected by direct methods and indirect consumers.
- Closed memory pools reject new work but still permit required deallocation.
- Destroyed graph nodes and invalid child graphs fail with a clear runtime error.
- CUDA default-stream singleton wrappers remain usable after
close().
- Tests cover Boolean state, safe object protocols, idempotent cleanup, active-method guards, conversion paths, and deallocation-stream failure atomicity.
Related
Summary
Add a consistent closed-state contract to
cuda.coreresources. Closed objects must fail before a NULL native handle reaches CUDA.Problem
Several
cuda.coreobjects reset a shared native handle inclose(), but their methods do not check that state. This can cause late CUDA errors, stale metadata, or invalid native access. Streams are the highest risk because CUDA can interpret a NULLCUstreamas the default stream. Work that targets a closed stream can therefore run on the wrong stream instead of failing.The same risk exists in event operations, buffer operations, memory pools, graph resources, compiler resources, texture resources, and cross-object conversion paths.
Required behavior
__bool__where an object has a clear open or closed state.Stream.from_handle(0)and an open zero-sizeBuffermust remain true.RuntimeError("<Type> has been closed")before an active operation uses a closed resource.close(), handle inspection, andrepr()safe.Stream.close()a no-op for CUDA default-stream tokens. These tokens use process-wide singleton wrappers and own no CUDA resource.deallocate()available after pool close so that outstanding buffers can complete cleanup.Scope
Apply the contract to these resources and their acceptance paths:
Stream,Event,Buffer, andManagedBuffer.Context,Program, andLinker.DeviceMemoryResource,PinnedMemoryResource, andManagedMemoryResource.GraphBuilder, executableGraph, destroyedGraphNodeobjects, and invalid childGraphDefinitionviews.OpaqueArray,MipmappedArray,TextureObject,SurfaceObject, andGraphicsResource.IPCAllocationHandle.Update central conversion and acceptance paths. This includes
Stream_accept, stream protocol conversion, event use in streams and graphs, buffer use in copy and managed-memory operations, DLPack, IPC, memory views, kernel arguments, virtual memory, and texture descriptors.A valid virtual graph entry node and a destroyed graph node both have a NULL
CUgraphNode. Add explicit state that distinguishes them. Reject destroyed nodes in graph construction and adjacency changes. Reject invalid child graph views in graph queries, embedding, instantiation, and updates.GraphMemoryResource.close()andProgramCacheResource.close()are out of scope because they do not create a closed native-resource state.Buffer deallocation stream
Buffer.set_deallocation_stream()andBuffer.close(stream=...)must reject a closedStreamor closedGraphBuilder. A failed update must leave the current deallocation stream unchanged. A closedBuffermust remain rejected as specified in #2600 and #2602.Acceptance criteria
Stream_acceptrejects every closed stream source.close().Related