Skip to content

Commit f7ac69f

Browse files
leofangclaude
andcommitted
Add explicit green context model: ctx.create_stream and ctx.resources
Switch from the v1.0 push model (dev.set_current + dev.create_stream) to the v1.1 explicit model (ctx.create_stream + ctx.resources) as the primary way to use green contexts. Context.create_stream(options): - Only supported on green contexts (raises on primary contexts). - Calls cuGreenCtxStreamCreate via the C++ handle layer — no push/pop of the context stack needed. - New C++ create_stream_handle_for_green_ctx() with function pointer for cuGreenCtxStreamCreate. Context.resources: - Returns a DeviceResources namespace querying this context's resources (cuCtxGetDevResource / cuGreenCtxGetDevResource), not the full device. - DeviceResources._init_from_ctx() uses the ContextHandle to dispatch. dev.set_current(green_ctx) still works but is not the recommended path. Tests rewritten to use the explicit model throughout. Push-model set_current kept as a single regression test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent aee88c6 commit f7ac69f

8 files changed

Lines changed: 275 additions & 76 deletions

File tree

cuda_core/cuda/core/_context.pyx

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ from collections.abc import Sequence
88
from dataclasses import dataclass
99

1010
from cuda.bindings cimport cydriver
11+
from cuda.core._device_resources cimport DeviceResources, SMResource, WorkqueueResource
1112
from cuda.core._device_resources import SMResource, WorkqueueResource
1213
from cuda.core._resource_handles cimport (
1314
ContextHandle,
1415
GreenCtxHandle,
16+
StreamHandle,
1517
as_cu,
1618
create_context_handle_from_green_ctx,
19+
create_stream_handle_for_green_ctx,
1720
get_context_green_ctx,
1821
get_last_error,
1922
as_intptr,
2023
as_py,
2124
)
22-
from cuda.core._utils.cuda_utils cimport HANDLE_RETURN
25+
from cuda.core._stream cimport Stream as cyStream
26+
from cuda.core._stream import StreamOptions
27+
from cuda.core._utils.cuda_utils cimport check_or_create_options, HANDLE_RETURN
2328

2429

2530
__all__ = ['Context', 'ContextOptions']
@@ -75,6 +80,75 @@ cdef class Context:
7580
return False
7681
return get_context_green_ctx(self._h_context).get() != NULL
7782

83+
@property
84+
def resources(self) -> DeviceResources:
85+
"""Query the hardware resources provisioned for this context.
86+
87+
For green contexts, returns the resources this context was created
88+
with (SM partition, workqueue config). For primary contexts, returns
89+
the full device resources.
90+
91+
Raises :class:`RuntimeError` if the context has been closed.
92+
"""
93+
if not self._h_context:
94+
raise RuntimeError("Cannot query resources on a closed context")
95+
return DeviceResources._init_from_ctx(self._h_context, self._device_id)
96+
97+
def create_stream(self, options: StreamOptions | None = None):
98+
"""Create a new stream bound to this green context.
99+
100+
This method is only available on green contexts. For primary
101+
contexts, use :meth:`Device.create_stream` instead.
102+
103+
Parameters
104+
----------
105+
options : :obj:`~_stream.StreamOptions`, optional
106+
Customizable dataclass for stream creation options.
107+
108+
Returns
109+
-------
110+
:obj:`~_stream.Stream`
111+
Newly created stream object.
112+
"""
113+
if not self._h_context:
114+
raise RuntimeError("Cannot create a stream on a closed context")
115+
if not self.is_green:
116+
raise RuntimeError(
117+
"Context.create_stream() is only supported on green contexts. "
118+
"Use Device.create_stream() for primary contexts."
119+
)
120+
121+
opts = check_or_create_options(StreamOptions, options, "Stream options")
122+
cdef bint nonblocking = opts.nonblocking
123+
cdef int priority = 0
124+
cdef int high, low
125+
cdef unsigned int flags
126+
127+
flags = (cydriver.CUstream_flags.CU_STREAM_NON_BLOCKING if nonblocking
128+
else cydriver.CUstream_flags.CU_STREAM_DEFAULT)
129+
130+
with nogil:
131+
HANDLE_RETURN(cydriver.cuCtxGetStreamPriorityRange(&high, &low))
132+
if opts.priority is not None:
133+
priority = opts.priority
134+
if not (low <= priority <= high):
135+
raise ValueError(f"priority={priority} is out of range [{low}, {high}]")
136+
else:
137+
priority = high
138+
139+
cdef GreenCtxHandle h_green = get_context_green_ctx(self._h_context)
140+
cdef StreamHandle h_stream = create_stream_handle_for_green_ctx(
141+
h_green, self._h_context, flags, priority)
142+
if not h_stream:
143+
HANDLE_RETURN(get_last_error())
144+
raise RuntimeError("Failed to create CUDA stream for green context")
145+
146+
cdef cyStream s = cyStream._from_handle(cyStream, h_stream)
147+
s._nonblocking = int(nonblocking)
148+
s._priority = priority
149+
s._device_id = self._device_id
150+
return s
151+
78152
cpdef close(self):
79153
"""Release this context wrapper's underlying CUDA handles."""
80154
cdef cydriver.CUcontext current_ctx

cuda_core/cuda/core/_cpp/resource_handles.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ decltype(&cuGreenCtxDestroy) p_cuGreenCtxDestroy = nullptr;
3434
decltype(&cuCtxFromGreenCtx) p_cuCtxFromGreenCtx = nullptr;
3535
decltype(&cuDevResourceGenerateDesc) p_cuDevResourceGenerateDesc = nullptr;
3636

37+
decltype(&cuGreenCtxStreamCreate) p_cuGreenCtxStreamCreate = nullptr;
38+
3739
decltype(&cuStreamCreateWithPriority) p_cuStreamCreateWithPriority = nullptr;
3840
decltype(&cuStreamDestroy) p_cuStreamDestroy = nullptr;
3941

@@ -427,6 +429,34 @@ StreamHandle create_stream_handle(const ContextHandle& h_ctx, unsigned int flags
427429
return h;
428430
}
429431

432+
StreamHandle create_stream_handle_for_green_ctx(
433+
const GreenCtxHandle& h_green, const ContextHandle& h_ctx,
434+
unsigned int flags, int priority) {
435+
if (!p_cuGreenCtxStreamCreate) {
436+
err = CUDA_ERROR_NOT_SUPPORTED;
437+
return {};
438+
}
439+
440+
GILReleaseGuard gil;
441+
CUstream stream;
442+
if (CUDA_SUCCESS != (err = p_cuGreenCtxStreamCreate(&stream, as_cu(h_green), flags, priority))) {
443+
return {};
444+
}
445+
446+
auto box = std::shared_ptr<const StreamBox>(
447+
new StreamBox{stream, h_ctx},
448+
[](const StreamBox* b) {
449+
stream_registry.unregister_handle(b->resource);
450+
GILReleaseGuard gil;
451+
p_cuStreamDestroy(b->resource);
452+
delete b;
453+
}
454+
);
455+
StreamHandle h(box, &box->resource);
456+
stream_registry.register_handle(stream, h);
457+
return h;
458+
}
459+
430460
StreamHandle create_stream_handle_ref(CUstream stream) {
431461
if (auto h = stream_registry.lookup(stream)) {
432462
return h;

cuda_core/cuda/core/_cpp/resource_handles.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ extern decltype(&cuGreenCtxDestroy) p_cuGreenCtxDestroy;
6464
extern decltype(&cuCtxFromGreenCtx) p_cuCtxFromGreenCtx;
6565
extern decltype(&cuDevResourceGenerateDesc) p_cuDevResourceGenerateDesc;
6666

67+
extern decltype(&cuGreenCtxStreamCreate) p_cuGreenCtxStreamCreate;
68+
6769
extern decltype(&cuStreamCreateWithPriority) p_cuStreamCreateWithPriority;
6870
extern decltype(&cuStreamDestroy) p_cuStreamDestroy;
6971

@@ -213,6 +215,12 @@ StreamHandle create_stream_handle_ref(CUstream stream);
213215
// The owner is responsible for keeping the stream's context alive.
214216
StreamHandle create_stream_handle_with_owner(CUstream stream, PyObject* owner);
215217

218+
// Create an owning stream from a green context (calls cuGreenCtxStreamCreate).
219+
// The returned stream handle depends on the green ctx's ContextHandle.
220+
StreamHandle create_stream_handle_for_green_ctx(
221+
const GreenCtxHandle& h_green, const ContextHandle& h_ctx,
222+
unsigned int flags, int priority);
223+
216224
// Return the context dependency associated with a stream handle, if any.
217225
ContextHandle get_stream_context(const StreamHandle& h) noexcept;
218226

cuda_core/cuda/core/_device_resources.pxd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
from cuda.bindings cimport cydriver
6+
from cuda.core._resource_handles cimport ContextHandle, GreenCtxHandle
67

78

89
cdef class SMResource:
@@ -38,7 +39,13 @@ cdef class WorkqueueResource:
3839
cdef class DeviceResources:
3940
cdef:
4041
int _device_id
42+
ContextHandle _h_context # NULL for device-level queries
4143
object __weakref__
4244

4345
@staticmethod
4446
cdef DeviceResources _init(int device_id)
47+
48+
@staticmethod
49+
cdef DeviceResources _init_from_ctx(ContextHandle h_context, int device_id)
50+
51+
cdef inline int _query_sm(self, cydriver.CUdevResource* res) except?-1 nogil

cuda_core/cuda/core/_device_resources.pyx

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ from libc.stdlib cimport free, malloc
1212
from libc.string cimport memset
1313

1414
from cuda.bindings cimport cydriver
15+
from cuda.core._resource_handles cimport ContextHandle, GreenCtxHandle, as_cu, get_context_green_ctx
1516
from cuda.core._utils.cuda_utils cimport check_or_create_options, HANDLE_RETURN
1617
from cuda.core._utils.cuda_utils import is_sequence
1718
from cuda.core._utils.version cimport cy_binding_version, cy_driver_version
@@ -480,53 +481,99 @@ cdef class WorkqueueResource:
480481

481482

482483
cdef class DeviceResources:
483-
"""Namespace for hardware resource query. Not user-constructible."""
484+
"""Namespace for hardware resource query. Not user-constructible.
485+
486+
When obtained via ``dev.resources``, queries return full device resources.
487+
When obtained via ``ctx.resources``, queries return the resources
488+
provisioned for that context.
489+
"""
484490

485491
def __init__(self, *args, **kwargs):
486492
raise RuntimeError(
487493
"DeviceResources cannot be instantiated directly. "
488-
"Use dev.resources."
494+
"Use dev.resources or ctx.resources."
489495
)
490496

491497
@staticmethod
492498
cdef DeviceResources _init(int device_id):
493499
cdef DeviceResources self = DeviceResources.__new__(DeviceResources)
494500
self._device_id = device_id
501+
# _h_context is default empty — queries use cuDeviceGetDevResource
495502
return self
496503

504+
@staticmethod
505+
cdef DeviceResources _init_from_ctx(ContextHandle h_context, int device_id):
506+
cdef DeviceResources self = DeviceResources.__new__(DeviceResources)
507+
self._device_id = device_id
508+
self._h_context = h_context
509+
return self
510+
511+
cdef inline int _query_sm(self, cydriver.CUdevResource* res) except?-1 nogil:
512+
"""Query SM resource from either device or context."""
513+
cdef GreenCtxHandle h_green
514+
if self._h_context:
515+
h_green = get_context_green_ctx(self._h_context)
516+
if h_green:
517+
HANDLE_RETURN(cydriver.cuGreenCtxGetDevResource(
518+
as_cu(h_green), res,
519+
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
520+
))
521+
else:
522+
HANDLE_RETURN(cydriver.cuCtxGetDevResource(
523+
as_cu(self._h_context), res,
524+
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
525+
))
526+
else:
527+
HANDLE_RETURN(cydriver.cuDeviceGetDevResource(
528+
<cydriver.CUdevice>self._device_id, res,
529+
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
530+
))
531+
return 0
532+
497533
@property
498534
def sm(self) -> SMResource:
499-
"""Query SM resources from this device."""
535+
"""Query SM resources."""
500536
_check_green_ctx_support()
501537
cdef cydriver.CUdevResource res
502538
with nogil:
503-
HANDLE_RETURN(cydriver.cuDeviceGetDevResource(
504-
<cydriver.CUdevice>self._device_id,
505-
&res,
506-
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_SM,
507-
))
539+
self._query_sm(&res)
508540
return SMResource._from_dev_resource(res, self._device_id)
509541

510542
@property
511543
def workqueue(self) -> WorkqueueResource:
512-
"""Query workqueue resources from this device."""
544+
"""Query workqueue resources."""
513545
_check_green_ctx_support()
514546
_check_workqueue_support()
515547
cdef cydriver.CUdevResource _wq_config
516548
cdef cydriver.CUdevResource _wq
517549

518550
IF CUDA_CORE_BUILD_MAJOR >= 13:
519-
with nogil:
520-
HANDLE_RETURN(cydriver.cuDeviceGetDevResource(
521-
<cydriver.CUdevice>self._device_id,
522-
&_wq_config,
523-
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
524-
))
525-
HANDLE_RETURN(cydriver.cuDeviceGetDevResource(
526-
<cydriver.CUdevice>self._device_id,
527-
&_wq,
528-
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
529-
))
551+
if self._h_context:
552+
# Context-level query
553+
with nogil:
554+
HANDLE_RETURN(cydriver.cuCtxGetDevResource(
555+
as_cu(self._h_context),
556+
&_wq_config,
557+
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
558+
))
559+
HANDLE_RETURN(cydriver.cuCtxGetDevResource(
560+
as_cu(self._h_context),
561+
&_wq,
562+
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
563+
))
564+
else:
565+
# Device-level query
566+
with nogil:
567+
HANDLE_RETURN(cydriver.cuDeviceGetDevResource(
568+
<cydriver.CUdevice>self._device_id,
569+
&_wq_config,
570+
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE_CONFIG,
571+
))
572+
HANDLE_RETURN(cydriver.cuDeviceGetDevResource(
573+
<cydriver.CUdevice>self._device_id,
574+
&_wq,
575+
cydriver.CUdevResourceType.CU_DEV_RESOURCE_TYPE_WORKQUEUE,
576+
))
530577
return WorkqueueResource._from_dev_resources(_wq_config, _wq)
531578
ELSE:
532579
raise NotImplementedError(

cuda_core/cuda/core/_resource_handles.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ cdef ContextHandle get_current_context() except+ nogil
123123
# Stream handles
124124
cdef StreamHandle create_stream_handle(
125125
const ContextHandle& h_ctx, unsigned int flags, int priority) except+ nogil
126+
cdef StreamHandle create_stream_handle_for_green_ctx(
127+
const GreenCtxHandle& h_green, const ContextHandle& h_ctx,
128+
unsigned int flags, int priority) except+ nogil
126129
cdef StreamHandle create_stream_handle_ref(cydriver.CUstream stream) except+ nogil
127130
cdef StreamHandle create_stream_handle_with_owner(cydriver.CUstream stream, object owner) except+ nogil
128131
cdef ContextHandle get_stream_context(const StreamHandle& h) noexcept nogil

cuda_core/cuda/core/_resource_handles.pyx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ cdef extern from "_cpp/resource_handles.hpp" namespace "cuda_core":
7272
# Stream handles
7373
StreamHandle create_stream_handle "cuda_core::create_stream_handle" (
7474
const ContextHandle& h_ctx, unsigned int flags, int priority) except+ nogil
75+
StreamHandle create_stream_handle_for_green_ctx "cuda_core::create_stream_handle_for_green_ctx" (
76+
const GreenCtxHandle& h_green, const ContextHandle& h_ctx,
77+
unsigned int flags, int priority) except+ nogil
7578
StreamHandle create_stream_handle_ref "cuda_core::create_stream_handle_ref" (
7679
cydriver.CUstream stream) except+ nogil
7780
StreamHandle create_stream_handle_with_owner "cuda_core::create_stream_handle_with_owner" (
@@ -239,6 +242,7 @@ cdef extern from "_cpp/resource_handles.hpp" namespace "cuda_core":
239242
void* p_cuGreenCtxDestroy "reinterpret_cast<void*&>(cuda_core::p_cuGreenCtxDestroy)"
240243
void* p_cuCtxFromGreenCtx "reinterpret_cast<void*&>(cuda_core::p_cuCtxFromGreenCtx)"
241244
void* p_cuDevResourceGenerateDesc "reinterpret_cast<void*&>(cuda_core::p_cuDevResourceGenerateDesc)"
245+
void* p_cuGreenCtxStreamCreate "reinterpret_cast<void*&>(cuda_core::p_cuGreenCtxStreamCreate)"
242246

243247
# Stream
244248
void* p_cuStreamCreateWithPriority "reinterpret_cast<void*&>(cuda_core::p_cuStreamCreateWithPriority)"
@@ -320,6 +324,7 @@ p_cuGreenCtxCreate = _get_optional_driver_fn("cuGreenCtxCreate")
320324
p_cuGreenCtxDestroy = _get_optional_driver_fn("cuGreenCtxDestroy")
321325
p_cuCtxFromGreenCtx = _get_optional_driver_fn("cuCtxFromGreenCtx")
322326
p_cuDevResourceGenerateDesc = _get_optional_driver_fn("cuDevResourceGenerateDesc")
327+
p_cuGreenCtxStreamCreate = _get_optional_driver_fn("cuGreenCtxStreamCreate")
323328

324329
# Stream
325330
p_cuStreamCreateWithPriority = _get_driver_fn("cuStreamCreateWithPriority")

0 commit comments

Comments
 (0)