Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cuda_core/cuda/core/_context.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ cdef class Context:
cdef Context _from_green_ctx(type cls, GreenCtxHandle h_green_ctx, int device_id)

cpdef close(self)


cdef inline int Context_check_open(Context self) except -1:
if not self._h_context:
raise RuntimeError("Context has been closed")
return 0
4 changes: 4 additions & 0 deletions cuda_core/cuda/core/_context.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class Context:
def _handle(self) -> cuda.bindings.driver.CUcontext | None:
...

@property
def is_closed(self) -> bool:
"""Whether this context has been closed."""

@property
def is_green(self) -> bool:
"""True if this context was created from device resources."""
Expand Down
11 changes: 7 additions & 4 deletions cuda_core/cuda/core/_context.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ cdef class Context:
def _handle(self) -> cuda.bindings.driver.CUcontext | None:
return self.handle

@property
def is_closed(self) -> bool:
"""Whether this context has been closed."""
return self._h_context.get() == NULL

@property
def is_green(self) -> bool:
"""True if this context was created from device resources."""
Expand All @@ -91,8 +96,7 @@ cdef class Context:

Raises :class:`RuntimeError` if the context has been closed.
"""
if not self._h_context:
raise RuntimeError("Cannot query resources on a closed context")
Context_check_open(self)
return DeviceResources._init_from_ctx(self._h_context, self._device_id)

def create_stream(self, options: object = None) -> Stream:
Expand All @@ -111,8 +115,7 @@ cdef class Context:
:obj:`~_stream.Stream`
Newly created stream object.
"""
if not self._h_context:
raise RuntimeError("Cannot create a stream on a closed context")
Context_check_open(self)
if not self.is_green:
raise RuntimeError(
"Context.create_stream() is only supported on green contexts. "
Expand Down
3 changes: 2 additions & 1 deletion cuda_core/cuda/core/_device.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ from libcpp.vector cimport vector

import threading

from cuda.core._context cimport Context
from cuda.core._context cimport Context, Context_check_open
from cuda.core._context import ContextOptions
from cuda.core._device_resources cimport DeviceResources, SMResource, WorkqueueResource
from cuda.core._event cimport Event as cyEvent
Expand Down Expand Up @@ -1278,6 +1278,7 @@ class Device:
if ctx is not None:
# TODO: revisit once Context is cythonized
assert_type(ctx, Context)
Context_check_open(ctx)
if ctx._device_id != self._device_id:
raise RuntimeError(
"the provided context was created on the device with"
Expand Down
7 changes: 7 additions & 0 deletions cuda_core/cuda/core/_event.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ cdef class Event:
cdef Event _from_handle(EventHandle h_event)

cpdef close(self)


cdef Event Event_accept(object arg)
cdef inline int Event_check_open(Event self) except -1:
if not self._h_event:
raise RuntimeError("Event has been closed")
return 0
4 changes: 4 additions & 0 deletions cuda_core/cuda/core/_event.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class Event:
def __init__(self, *args, **kwargs) -> None:
...

@property
def is_closed(self) -> bool:
"""Whether this event has been closed."""

def __isub__(self, other: object):
...

Expand Down
26 changes: 24 additions & 2 deletions cuda_core/cuda/core/_event.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ cdef class Event:
"""
self._h_event.reset()

@property
def is_closed(self) -> bool:
"""Whether this event has been closed."""
return self._h_event.get() == NULL

def __isub__(self, other: object):
return NotImplemented

Expand All @@ -165,14 +170,16 @@ cdef class Event:

def __sub__(self, other: Event) -> float:
# return self - other (in milliseconds)
Event_check_open(self)
cdef Event other_event = Event_accept(other)
cdef float timing
with nogil:
err = cydriver.cuEventElapsedTime(&timing, as_cu((<Event>other)._h_event), as_cu(self._h_event))
err = cydriver.cuEventElapsedTime(&timing, as_cu(other_event._h_event), as_cu(self._h_event))
if err == 0:
return timing
else:
if err == cydriver.CUresult.CUDA_ERROR_INVALID_HANDLE:
if not self.is_timing_enabled or not other.is_timing_enabled:
if not self.is_timing_enabled or not other_event.is_timing_enabled:
explanation = (
"Both Events must be created with timing enabled in order to subtract them; "
"use EventOptions(timing_enabled=True) when creating both events."
Expand Down Expand Up @@ -208,6 +215,7 @@ cdef class Event:
@property
def ipc_descriptor(self) -> IPCEventDescriptor:
"""Descriptor for sharing this event with other processes."""
Event_check_open(self)
if self._ipc_descriptor is not None:
return self._ipc_descriptor
if not self.is_ipc_enabled:
Expand Down Expand Up @@ -255,18 +263,21 @@ cdef class Event:
@property
def is_ipc_enabled(self) -> bool:
"""Return True if the event can be shared across process boundaries, otherwise False."""
Event_check_open(self)
return get_event_ipc_enabled(self._h_event)

@property
def is_timing_enabled(self) -> bool:
"""Return True if the event records timing data, otherwise False."""
Event_check_open(self)
return get_event_timing_enabled(self._h_event)

@property
def is_blocking_sync(self) -> bool:
"""Return True if the event uses blocking synchronization (the CPU
thread blocks on :meth:`sync` instead of busy-waiting), otherwise False.
"""
Event_check_open(self)
return get_event_is_blocking_sync(self._h_event)

def sync(self) -> None:
Expand All @@ -278,12 +289,14 @@ cdef class Event:
thread busy-waits until the event has completed.

"""
Event_check_open(self)
with nogil:
HANDLE_RETURN(cydriver.cuEventSynchronize(as_cu(self._h_event)))

@property
def is_done(self) -> bool:
"""Return True if all captured works have been completed, otherwise False."""
Event_check_open(self)
with nogil:
result = cydriver.cuEventQuery(as_cu(self._h_event))
if result == cydriver.CUresult.CUDA_SUCCESS:
Expand Down Expand Up @@ -314,6 +327,7 @@ cdef class Event:
context is set current after a event is created.

"""
Event_check_open(self)
cdef int dev_id = get_event_device_id(self._h_event)
if dev_id >= 0:
from ._device import Device # avoid circular import
Expand All @@ -322,11 +336,19 @@ cdef class Event:
@property
def context(self) -> Context:
"""Return the :obj:`~_context.Context` associated with this event."""
Event_check_open(self)
cdef ContextHandle h_ctx = get_event_context(self._h_event)
cdef int dev_id = get_event_device_id(self._h_event)
if h_ctx and dev_id >= 0:
return Context._from_handle(Context, h_ctx, dev_id)

cdef Event Event_accept(object arg):
if not isinstance(arg, Event):
raise TypeError(f"Event expected, got {type(arg).__name__}")
cdef Event event = <Event>arg
Event_check_open(event)
return event


cdef class IPCEventDescriptor:
"""Serializable object describing an event that can be shared between processes."""
Expand Down
4 changes: 4 additions & 0 deletions cuda_core/cuda/core/_graphics.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ class GraphicsResource:
def handle(self) -> int:
"""The raw ``CUgraphicsResource`` handle as a Python int."""

@property
def is_closed(self) -> bool:
"""Whether this graphics resource has been closed."""

@property
def resource_handle(self) -> int:
"""Alias for :attr:`handle`."""
Expand Down
17 changes: 13 additions & 4 deletions cuda_core/cuda/core/_graphics.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ def _parse_register_flags(flags: str | Sequence[str] | None) -> int:
return result


cdef inline int GraphicsResource_check_open(GraphicsResource self) except -1:
if not self._handle:
raise RuntimeError("GraphicsResource has been closed")
return 0


cdef class GraphicsResource:
"""RAII wrapper for a CUDA graphics resource (``CUgraphicsResource``).

Expand Down Expand Up @@ -251,8 +257,7 @@ cdef class GraphicsResource:
"""
cdef cydriver.CUdeviceptr dev_ptr = 0
cdef size_t size = 0
if not self._handle:
raise RuntimeError("GraphicsResource has been closed")
GraphicsResource_check_open(self)
if self._get_mapped_buffer() is not None:
raise RuntimeError("GraphicsResource is already mapped")

Expand Down Expand Up @@ -294,8 +299,7 @@ cdef class GraphicsResource:
CUDAError
If the unmapping fails.
"""
if not self._handle:
raise RuntimeError("GraphicsResource has been closed")
GraphicsResource_check_open(self)
cdef object buf_obj = self._get_mapped_buffer()
if buf_obj is None:
raise RuntimeError("GraphicsResource is not mapped")
Expand Down Expand Up @@ -347,6 +351,11 @@ cdef class GraphicsResource:
"""The raw ``CUgraphicsResource`` handle as a Python int."""
return as_intptr(self._handle)

@property
def is_closed(self) -> bool:
"""Whether this graphics resource has been closed."""
return self._handle.get() == NULL

@property
def resource_handle(self) -> int:
"""Alias for :attr:`handle`."""
Expand Down
3 changes: 3 additions & 0 deletions cuda_core/cuda/core/_kernel_arg_handler.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ from typing import Sequence, Any
import numpy

from cuda.core._memory import Buffer
from cuda.core._memory._buffer cimport Buffer as cyBuffer, Buffer_check_open
from cuda.core._tensor_map import TensorMapDescriptor as _TensorMapDescriptor_py
from cuda.core._tensor_map cimport TensorMapDescriptor
from cuda.core.graph._graph_definition cimport GraphCondition
Expand Down Expand Up @@ -282,6 +283,7 @@ cdef class ParamHolder:
for i, arg in enumerate(kernel_args):
arg_type = type(arg)
if arg_type is Buffer:
Buffer_check_open(<cyBuffer>arg)
# we need the address of where the actual buffer address is stored
if type(arg.handle) is int:
# see note below on handling int arguments
Expand Down Expand Up @@ -327,6 +329,7 @@ cdef class ParamHolder:
continue
# If no exact types are found, fallback to slower `isinstance` check
elif isinstance(arg, Buffer):
Buffer_check_open(<cyBuffer>arg)
if isinstance(arg.handle, int):
prepare_arg[intptr_t](self.data, self.data_addresses, arg.handle, i)
continue
Expand Down
4 changes: 4 additions & 0 deletions cuda_core/cuda/core/_linker.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class Linker:
def __init__(self, options: LinkerOptions | None=None, *object_codes: ObjectCode):
...

@property
def is_closed(self) -> bool:
"""Whether this linker has been closed."""

def link(self, target_type: ObjectCodeFormatType | str) -> ObjectCode:
"""Link the provided object codes into a single output of the specified target type.

Expand Down
17 changes: 17 additions & 0 deletions cuda_core/cuda/core/_linker.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ LinkerHandleT = Union["cuda.bindings.nvjitlink.nvJitLinkHandle", "cuda.bindings.
# Principal class
# =============================================================================


cdef inline int Linker_check_open(Linker self) except -1:
if self.is_closed:
raise RuntimeError("Linker has been closed")
return 0


cdef class Linker:
"""Represent a linking machinery to link one or more object codes into
:class:`~cuda.core.ObjectCode`.
Expand All @@ -81,6 +88,13 @@ cdef class Linker:
def __init__(self, *object_codes: ObjectCode, options: LinkerOptions | None = None):
Linker_init(self, object_codes, options)

@property
def is_closed(self) -> bool:
"""Whether this linker has been closed."""
if self._use_nvjitlink:
return self._nvjitlink_handle.get() == NULL
return self._culink_handle.get() == NULL

def link(self, target_type: ObjectCodeFormatType | str) -> ObjectCode:
"""Link the provided object codes into a single output of the specified target type.

Expand All @@ -99,6 +113,7 @@ cdef class Linker:
Ensure that input object codes were compiled with appropriate
flags for linking (e.g., relocatable device code enabled).
"""
Linker_check_open(self)
return Linker_link(self, str(target_type))

def get_error_log(self) -> str:
Expand All @@ -112,6 +127,7 @@ cdef class Linker:
# After link(), the decoded log is cached here.
if self._error_log is not None:
return self._error_log
Linker_check_open(self)
cdef cynvjitlink.nvJitLinkHandle c_h
cdef size_t c_log_size = 0
cdef char* c_log_ptr
Expand All @@ -138,6 +154,7 @@ cdef class Linker:
# After link(), the decoded log is cached here.
if self._info_log is not None:
return self._info_log
Linker_check_open(self)
cdef cynvjitlink.nvJitLinkHandle c_h
cdef size_t c_log_size = 0
cdef char* c_log_ptr
Expand Down
6 changes: 6 additions & 0 deletions cuda_core/cuda/core/_memory/_buffer.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ cdef Buffer Buffer_from_deviceptr_handle(
# prefetch_batch, discard_batch, discard_prefetch_batch). `single_hint`
# names the per-buffer API to use instead when a bare Buffer is passed.
cdef tuple Buffer_coerce_batch(object buffers, str what, str single_hint)


cdef inline int Buffer_check_open(Buffer self) except -1:
if not self._h_ptr:
raise RuntimeError("Buffer has been closed")
return 0
4 changes: 4 additions & 0 deletions cuda_core/cuda/core/_memory/_buffer.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ class Buffer:
handle, call ``int(Buffer.handle)``.
"""

@property
def is_closed(self) -> bool:
"""Whether this buffer has been closed."""

def __eq__(self, other: object) -> bool:
...

Expand Down
Loading
Loading