Skip to content

Commit b29e21d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into stubgen-0.2.19-update
2 parents 1b92db9 + 6c5ecf9 commit b29e21d

26 files changed

Lines changed: 1586 additions & 363 deletions

AGENTS.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,30 @@ guide for package-specific conventions and workflows.
1414

1515
# Pull requests
1616

17-
**Never push branches or commits to the canonical upstream repository. Treat
18-
it as read-only.** Branch creation and pushes for pull-request work must go to
19-
an approved fork associated with the contributor. The fork may be owned by the
20-
contributor's personal account or by an organization.
21-
22-
Before pushing, run `git remote -v` and confirm that the intended push remote
23-
points to a fork of the pull-request base, not to the base repository itself.
24-
Compare complete `OWNER/REPOSITORY` names; do not rely on remote names such as
25-
`origin` or `upstream`, or on the owner alone. Do not use `git push upstream`
26-
or any command that writes to the upstream remote.
17+
Treat the canonical upstream repository as read-only by default. For normal
18+
pull-request work, push branches and commits to an approved fork associated
19+
with the contributor. The fork may be owned by the contributor's personal
20+
account or by an organization.
21+
22+
Before any push, run `git remote -v` and verify the complete
23+
`OWNER/REPOSITORY` of the intended destination. For normal pull-request work,
24+
confirm that the destination is a fork of the pull-request base. Do not rely
25+
on remote names such as `origin` or `upstream`, or on the owner alone.
26+
27+
An upstream push is allowed when the user explicitly requests it and provides
28+
a rationale for why the upstream repository is needed, such as testing
29+
`.github/workflows`, triggering CI from a designated upstream ref, or other
30+
infrastructure work.
31+
32+
For an authorized upstream push, verify the exact source and destination refs
33+
against the user's request. If the repository and refspec are unambiguous,
34+
proceed; do not require the user to perform the push manually solely because
35+
the destination is upstream.
36+
37+
Authorization is limited to the requested ref update. It does not authorize
38+
pushing to a default or protected branch, force-pushing, creating tags, or
39+
deleting refs unless the user separately and explicitly requests those
40+
operations.
2741

2842

2943
# General

cuda_bindings/tests/cython/build_tests.bat

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ REM SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIA
44
REM SPDX-License-Identifier: Apache-2.0
55

66
setlocal
7-
set CL=%CL% /I"%CUDA_HOME%\include"
8-
REM Use -j 1 to side-step any process-pool issues and ensure deterministic single-threaded builds
9-
cythonize -3 -j 1 -i -Xfreethreading_compatible=True %~dp0test_*.pyx
10-
endlocal
7+
set CL=%CL% /I"%CUDA_HOME%\include"
8+
REM The Python driver provides Cython's .pxd include path and builds in this
9+
REM directory so Windows does not duplicate the checkout path in link outputs.
10+
python "%~dp0build_tests.py"
11+
set "BUILD_RESULT=%ERRORLEVEL%"
12+
endlocal & exit /b %BUILD_RESULT%

cuda_bindings/tests/cython/build_tests.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ def _bindings_source_root() -> Path:
3434

3535
def main() -> None:
3636
script_dir = Path(__file__).resolve().parent
37-
pyx_files = sorted(str(p) for p in script_dir.glob("test_*.pyx"))
37+
# Avoid appending the absolute checkout path under build/temp: the
38+
# concatenated path can exceed Windows' path limit. These files are siblings.
39+
os.chdir(script_dir)
40+
pyx_files = sorted(p.name for p in script_dir.glob("test_*.pyx"))
3841
if not pyx_files:
3942
raise SystemExit(f"no test_*.pyx files under {script_dir}")
4043

@@ -46,13 +49,8 @@ def main() -> None:
4649
compiler_directives={"freethreading_compatible": True},
4750
)
4851

49-
# `build_ext --inplace` places the compiled .so relative to the current
50-
# working directory, but pixi runs this task from the project root. pytest
51-
# imports each extension by bare module name (see test_cython.py), which
52-
# only resolves when the .so sits in tests/cython (the dir pytest puts on
53-
# sys.path). chdir here so the .so lands next to its .pyx regardless of the
54-
# invoking cwd.
55-
os.chdir(script_dir)
52+
# pytest imports each extension by bare module name (see test_cython.py),
53+
# so build in-place next to its .pyx regardless of the invoking cwd.
5654
sys.argv = [sys.argv[0], "build_ext", "--inplace"]
5755
setup(name="cuda_bindings_cython_tests", ext_modules=ext_modules)
5856

cuda_core/cuda/core/_cpp/resource_handles.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ decltype(&cuDevSmResourceSplit) p_cuDevSmResourceSplit = nullptr;
111111
void* p_cuDevSmResourceSplit = nullptr;
112112
#endif
113113

114+
// cuMemcpyWithAttributesAsync (13.2+ — may be null on older drivers/bindings)
115+
#if CUDA_VERSION >= 13020
116+
decltype(&cuMemcpyWithAttributesAsync) p_cuMemcpyWithAttributesAsync = nullptr;
117+
#else
118+
void* p_cuMemcpyWithAttributesAsync = nullptr;
119+
#endif
120+
114121
// NVRTC function pointers
115122
decltype(&nvrtcDestroyProgram) p_nvrtcDestroyProgram = nullptr;
116123

@@ -2834,4 +2841,25 @@ bool has_sm_resource_split() noexcept {
28342841
return p_cuDevSmResourceSplit != nullptr;
28352842
}
28362843

2844+
// ============================================================================
2845+
// cuMemcpyWithAttributesAsync wrapper
2846+
// ============================================================================
2847+
2848+
CUresult memcpy_with_attributes_async(CUdeviceptr dst, CUdeviceptr src, size_t size,
2849+
void* attr, CUstream hStream) {
2850+
#if CUDA_VERSION >= 13020
2851+
if (!p_cuMemcpyWithAttributesAsync) {
2852+
return CUDA_ERROR_NOT_SUPPORTED;
2853+
}
2854+
return p_cuMemcpyWithAttributesAsync(
2855+
dst, src, size, static_cast<CUmemcpyAttributes*>(attr), hStream);
2856+
#else
2857+
return CUDA_ERROR_NOT_SUPPORTED;
2858+
#endif
2859+
}
2860+
2861+
bool has_memcpy_with_attributes_async() noexcept {
2862+
return p_cuMemcpyWithAttributesAsync != nullptr;
2863+
}
2864+
28372865
} // namespace cuda_core

cuda_core/cuda/core/_cpp/resource_handles.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ extern decltype(&cuDevSmResourceSplit) p_cuDevSmResourceSplit;
146146
extern void* p_cuDevSmResourceSplit;
147147
#endif
148148

149+
// cuMemcpyWithAttributesAsync (13.2+ — may be null on older drivers/bindings)
150+
#if CUDA_VERSION >= 13020
151+
extern decltype(&cuMemcpyWithAttributesAsync) p_cuMemcpyWithAttributesAsync;
152+
#else
153+
// cuMemcpyWithAttributesAsync doesn't exist in CUDA < 13.2 headers, so use a
154+
// void* placeholder. The pointer is always null when built against older CUDA.
155+
extern void* p_cuMemcpyWithAttributesAsync;
156+
#endif
157+
149158
// ============================================================================
150159
// NVRTC function pointers
151160
//
@@ -1110,4 +1119,21 @@ CUresult sm_resource_split(CUdevResource* result, unsigned int nbGroups,
11101119
// Returns true if the cuDevSmResourceSplit function pointer is available.
11111120
bool has_sm_resource_split() noexcept;
11121121

1122+
// ============================================================================
1123+
// cuMemcpyWithAttributesAsync wrapper (13.2+)
1124+
//
1125+
// Calls through p_cuMemcpyWithAttributesAsync if available, otherwise returns
1126+
// CUDA_ERROR_NOT_SUPPORTED. This avoids a direct Cython cimport of the
1127+
// cydriver cdef function, which would fail at module init on cuda-bindings
1128+
// < 13.2 (see https://github.com/NVIDIA/cuda-python/issues/2063).
1129+
// ============================================================================
1130+
1131+
// attr is void* so the Cython declaration doesn't reference CUmemcpyAttributes
1132+
// (absent from cuda-bindings built against CUDA < 12.8). The C++ side casts it.
1133+
CUresult memcpy_with_attributes_async(CUdeviceptr dst, CUdeviceptr src, size_t size,
1134+
void* attr, CUstream hStream);
1135+
1136+
// Returns true if the cuMemcpyWithAttributesAsync function pointer is available.
1137+
bool has_memcpy_with_attributes_async() noexcept;
1138+
11131139
} // namespace cuda_core

cuda_core/cuda/core/_memory/_buffer.pyi

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import TypedDict
44

5+
from cuda.core._memory._copy_enums import CopyOptions
56
from cuda.core._memory._device_memory_resource import DeviceMemoryResource
67
from cuda.core._memory._ipc import IPCBufferDescriptor
78
from cuda.core._memory._pinned_memory_resource import PinnedMemoryResource
@@ -149,7 +150,7 @@ class Buffer:
149150
"""
150151
def __enter__(self): ...
151152
def __exit__(self, exc_type, exc_val, exc_tb): ...
152-
def copy_to(self, dst: Buffer | None=None, *, stream: Stream | GraphBuilder) -> Buffer:
153+
def copy_to(self, dst: Buffer | None=None, *, stream: Stream | GraphBuilder, options: CopyOptions | None=None) -> Buffer:
153154
"""Copy from this buffer to the dst buffer asynchronously on the given stream.
154155
155156
Copies the data from this buffer to the provided dst buffer.
@@ -164,9 +165,31 @@ class Buffer:
164165
stream : :obj:`~_stream.Stream` | :obj:`~graph.GraphBuilder`
165166
Keyword argument specifying the stream for the
166167
asynchronous copy
168+
options : :class:`~utils.CopyOptions`, optional
169+
Transfer hints (source access order, location hints, overlap mode).
170+
Honored when cuda.bindings and the driver are both CUDA 13.2 or
171+
newer. Not accepted with ``LEGACY_DEFAULT_STREAM``; use
172+
``PER_THREAD_DEFAULT_STREAM`` instead. Not accepted with a
173+
capturing stream either, since a graph cannot represent these
174+
attributes; use :meth:`graph.GraphNode.memcpy` for a plain,
175+
non-attributed copy node, or pass ``options=None``. On an older
176+
cuda.bindings/driver, ``src_access_order`` values of ``STREAM``
177+
and ``ANY`` are silently ignored; ``DURING_API_CALL`` raises
178+
instead of silently downgrading its guarantee.
179+
180+
Raises
181+
------
182+
TypeError
183+
If ``options`` is not a :class:`~utils.CopyOptions` instance, or
184+
if ``options`` is given together with ``LEGACY_DEFAULT_STREAM``
185+
or a stream currently in graph capture mode.
186+
RuntimeError
187+
If ``options.src_access_order`` is ``DURING_API_CALL`` and
188+
cuda.bindings or the driver is older than CUDA 13.2: falling
189+
back to a plain copy cannot honor that guarantee.
167190
168191
"""
169-
def copy_from(self, src: Buffer, *, stream: Stream | GraphBuilder) -> None:
192+
def copy_from(self, src: Buffer, *, stream: Stream | GraphBuilder, options: CopyOptions | None=None) -> None:
170193
"""Copy from the src buffer to this buffer asynchronously on the given stream.
171194
172195
Parameters
@@ -176,7 +199,28 @@ class Buffer:
176199
stream : :obj:`~_stream.Stream` | :obj:`~graph.GraphBuilder`
177200
Keyword argument specifying the stream for the
178201
asynchronous copy
202+
options : :class:`~utils.CopyOptions`, optional
203+
Transfer hints (source access order, location hints, overlap mode).
204+
Honored when cuda.bindings and the driver are both CUDA 13.2 or
205+
newer. Not accepted with ``LEGACY_DEFAULT_STREAM``; use
206+
``PER_THREAD_DEFAULT_STREAM`` instead. Not accepted with a
207+
capturing stream either, since a graph cannot represent these
208+
attributes; use :meth:`graph.GraphNode.memcpy` for a plain,
209+
non-attributed copy node, or pass ``options=None``. On an older
210+
cuda.bindings/driver, ``src_access_order`` values of ``STREAM``
211+
and ``ANY`` are silently ignored; ``DURING_API_CALL`` raises
212+
instead of silently downgrading its guarantee.
179213
214+
Raises
215+
------
216+
TypeError
217+
If ``options`` is not a :class:`~utils.CopyOptions` instance, or
218+
if ``options`` is given together with ``LEGACY_DEFAULT_STREAM``
219+
or a stream currently in graph capture mode.
220+
RuntimeError
221+
If ``options.src_access_order`` is ``DURING_API_CALL`` and
222+
cuda.bindings or the driver is older than CUDA 13.2: falling
223+
back to a plain copy cannot honor that guarantee.
180224
"""
181225
def fill(self, value: int | BufferProtocol, *, stream: Stream | GraphBuilder) -> None:
182226
"""Fill this buffer with a repeating byte pattern.

0 commit comments

Comments
 (0)