Skip to content

Commit b38e471

Browse files
committed
Merge remote-tracking branch 'upstream/main' into fix/adjacency-discard-annotation-typing
# Conflicts: # cuda_core/docs/source/release/1.3.0-notes.rst
2 parents 57c7a68 + d546939 commit b38e471

17 files changed

Lines changed: 277 additions & 117 deletions

File tree

‎.github/workflows/build-wheel.yml‎

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -223,19 +223,14 @@ jobs:
223223

224224
- name: Build cuda.bindings wheel
225225
if: ${{ env.BUILD_BINDINGS == 'true' }}
226-
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
226+
uses: pypa/cibuildwheel@e090b81e30c4d855ea63bf4b6e59204c09a101ae # v4.2.1
227227
with:
228228
package-dir: ./cuda_bindings/
229229
output-dir: ${{ env.CUDA_BINDINGS_ARTIFACTS_DIR }}
230230
env:
231231
CIBW_BUILD: ${{ env.CIBW_BUILD }}
232232
CIBW_BEFORE_BUILD_LINUX: 'python -m pip install --upgrade "pip>=25.3"'
233233
CIBW_BEFORE_BUILD_WINDOWS: 'python -m pip install --upgrade "pip>=25.3" delvewheel'
234-
# TODO: remove cpython-prerelease once 3.15 is officially supported
235-
# Allow CPython pre-release builds (currently 3.15 / 3.15t). This is a
236-
# no-op for stable Python versions because CIBW_BUILD still filters
237-
# the target version.
238-
CIBW_ENABLE: cpython-prerelease
239234
# CIBW mounts the host filesystem under /host
240235
CIBW_ENVIRONMENT_LINUX: >
241236
CUDA_PATH=/host/${{ env.CUDA_PATH }}
@@ -250,8 +245,6 @@ jobs:
250245
ACTIONS_RESULTS_URL=${{ env.ACTIONS_RESULTS_URL }}
251246
ACTIONS_CACHE_URL=${{ env.ACTIONS_CACHE_URL }}
252247
ACTIONS_CACHE_SERVICE_V2=${{ env.ACTIONS_CACHE_SERVICE_V2 }}
253-
SCCACHE_DIR=/host/${{ env.SCCACHE_DIR }}
254-
SCCACHE_CACHE_SIZE=${{ env.SCCACHE_CACHE_SIZE }}
255248
CIBW_ENVIRONMENT_WINDOWS: >
256249
CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})"
257250
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
@@ -330,19 +323,14 @@ jobs:
330323

331324
- name: Build cuda.core wheel
332325
if: ${{ env.BUILD_CORE == 'true' }}
333-
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
326+
uses: pypa/cibuildwheel@e090b81e30c4d855ea63bf4b6e59204c09a101ae # v4.2.1
334327
with:
335328
package-dir: ./cuda_core/
336329
output-dir: ${{ env.CUDA_CORE_ARTIFACTS_DIR }}
337330
env:
338331
CIBW_BUILD: ${{ env.CIBW_BUILD }}
339332
CIBW_BEFORE_BUILD_LINUX: 'python -m pip install --upgrade "pip>=25.3"'
340333
CIBW_BEFORE_BUILD_WINDOWS: 'python -m pip install --upgrade "pip>=25.3" delvewheel'
341-
# TODO: remove cpython-prerelease once 3.15 is officially supported
342-
# Allow CPython pre-release builds (currently 3.15 / 3.15t). This is a
343-
# no-op for stable Python versions because CIBW_BUILD still filters
344-
# the target version.
345-
CIBW_ENABLE: cpython-prerelease
346334
# CIBW mounts the host filesystem under /host
347335
CIBW_ENVIRONMENT_LINUX: >
348336
CUDA_PATH=/host/${{ env.CUDA_PATH }}
@@ -358,8 +346,6 @@ jobs:
358346
ACTIONS_RESULTS_URL=${{ env.ACTIONS_RESULTS_URL }}
359347
ACTIONS_CACHE_URL=${{ env.ACTIONS_CACHE_URL }}
360348
ACTIONS_CACHE_SERVICE_V2=${{ env.ACTIONS_CACHE_SERVICE_V2 }}
361-
SCCACHE_DIR=/host/${{ env.SCCACHE_DIR }}
362-
SCCACHE_CACHE_SIZE=${{ env.SCCACHE_CACHE_SIZE }}
363349
CIBW_ENVIRONMENT_WINDOWS: >
364350
CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})"
365351
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}
@@ -624,19 +610,14 @@ jobs:
624610
625611
- name: Build cuda.core wheel
626612
if: ${{ !inputs.single-cuda-major && env.BUILD_CORE == 'true' }}
627-
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
613+
uses: pypa/cibuildwheel@e090b81e30c4d855ea63bf4b6e59204c09a101ae # v4.2.1
628614
with:
629615
package-dir: ./cuda_core/
630616
output-dir: ${{ env.CUDA_CORE_ARTIFACTS_DIR }}
631617
env:
632618
CIBW_BUILD: ${{ env.CIBW_BUILD }}
633619
CIBW_BEFORE_BUILD_LINUX: 'python -m pip install --upgrade "pip>=25.3"'
634620
CIBW_BEFORE_BUILD_WINDOWS: 'python -m pip install --upgrade "pip>=25.3" delvewheel'
635-
# TODO: remove cpython-prerelease once 3.15 is officially supported
636-
# Allow CPython pre-release builds (currently 3.15 / 3.15t). This is a
637-
# no-op for stable Python versions because CIBW_BUILD still filters
638-
# the target version.
639-
CIBW_ENABLE: cpython-prerelease
640621
# CIBW mounts the host filesystem under /host
641622
CIBW_ENVIRONMENT_LINUX: >
642623
CUDA_PATH=/host/${{ env.CUDA_PATH }}
@@ -652,8 +633,6 @@ jobs:
652633
ACTIONS_RESULTS_URL=${{ env.ACTIONS_RESULTS_URL }}
653634
ACTIONS_CACHE_URL=${{ env.ACTIONS_CACHE_URL }}
654635
ACTIONS_CACHE_SERVICE_V2=${{ env.ACTIONS_CACHE_SERVICE_V2 }}
655-
SCCACHE_DIR=/host/${{ env.SCCACHE_DIR }}
656-
SCCACHE_CACHE_SIZE=${{ env.SCCACHE_CACHE_SIZE }}
657636
CIBW_ENVIRONMENT_WINDOWS: >
658637
CUDA_PATH="$(cygpath -w ${{ env.CUDA_PATH }})"
659638
CUDA_PYTHON_PARALLEL_LEVEL=${{ env.CUDA_PYTHON_PARALLEL_LEVEL }}

‎ci/tools/env-vars‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ if [[ "${1}" == "build" ]]; then
5656
echo "BUILD_CUDA_MAJOR=${BUILD_CUDA_MAJOR}" >> $GITHUB_ENV
5757
echo "BUILD_PREV_CUDA_MAJOR=$((${BUILD_CUDA_MAJOR} - 1))" >> $GITHUB_ENV
5858
CUDA_BINDINGS_ARTIFACT_BASENAME="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda${CUDA_VER}-${HOST_PLATFORM}"
59-
# Enforce an explicit cache dir so that we can reuse this path later
60-
echo "SCCACHE_DIR=${HOME}/.cache/sccache" >> $GITHUB_ENV
61-
echo "SCCACHE_CACHE_SIZE=1G" >> $GITHUB_ENV
6259
elif [[ "${1}" == "test" ]]; then
6360
BUILD_CUDA_MAJOR="$(cut -d '.' -f 1 <<< ${BUILD_CUDA_VER})"
6461
TEST_CUDA_MAJOR="$(cut -d '.' -f 1 <<< ${CUDA_VER})"

‎cuda_bindings/build_hooks.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def _build_cuda_bindings(debug=False):
185185
extra_compile_args += ["-g", "-O0"]
186186
extra_compile_args += ["-D _GLIBCXX_ASSERTIONS"]
187187
else:
188-
extra_compile_args += ["-O3"]
188+
extra_compile_args += ["-g0", "-O3"]
189189
extra_link_args += ["-Wl,--strip-all"]
190190
if compile_for_coverage:
191191
# CYTHON_TRACE_NOGIL indicates to trace nogil functions. It is not

‎cuda_bindings/tests/nvml/test_device.py‎

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -198,26 +198,31 @@ def test_device_get_samples_zero_result_returns_tuple(all_devices, subtests):
198198
even when there are zero samples to report, instead of a bare Sample instance.
199199
"""
200200
for device in all_devices:
201-
with subtests.test(device_index=nvml.device_get_index(device)):
202-
with unsupported_before(device, None):
203-
# _FUTURE_TIMESTAMP is newer than any sample can ever be, so the
204-
# zero-sample result is deterministic and cannot race with a
205-
# newly arriving sample (unlike querying "now" and re-querying).
201+
with subtests.test(device_index=nvml.device_get_index(device)), unsupported_before(device, None):
202+
last_seen_timestamp = 0
203+
for _ in range(3):
206204
try:
207205
result = nvml.device_get_samples(
208-
device, nvml.SamplingType.GPU_UTILIZATION_SAMPLES, _FUTURE_TIMESTAMP
206+
device, nvml.SamplingType.GPU_UTILIZATION_SAMPLES, last_seen_timestamp
209207
)
210208
except nvml.NotFoundError:
211209
# Some drivers report NotFoundError instead of a zero-sample
212-
# SUCCESS when there is nothing newer than the timestamp;
213-
# that is also an acceptable (non-crashing) outcome.
214-
continue
210+
# SUCCESS when there is nothing newer than the timestamp.
211+
break
215212

216-
assert isinstance(result, tuple)
217-
assert len(result) == 2
218-
sample_val_type, samples = result
219-
assert isinstance(sample_val_type, int)
220-
assert len(samples) == 0
213+
assert isinstance(result, tuple)
214+
assert len(result) == 2
215+
sample_val_type, samples = result
216+
assert isinstance(sample_val_type, int)
217+
if len(samples) == 0:
218+
break
219+
220+
# NVML documents zero or a timestamp from a previous query.
221+
# Advance to the newest returned sample and try to observe the
222+
# zero-result path before another sample arrives.
223+
last_seen_timestamp = max(sample.time_stamp for sample in samples)
224+
else:
225+
pytest.skip("NVML continued producing samples before an empty result could be observed")
221226

222227

223228
def _check_vgpu_type_id_list(type_ids):

‎cuda_core/build_hooks.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ def module_names():
281281
extra_compile_args += ["-g", "-O0"]
282282
extra_compile_args += ["-D _GLIBCXX_ASSERTIONS"]
283283
else:
284-
extra_compile_args += ["-O2"]
284+
extra_compile_args += ["-g0", "-O2"]
285285
extra_link_args += ["-Wl,--strip-all"]
286286
if COMPILE_FOR_COVERAGE:
287287
# CYTHON_TRACE_NOGIL indicates to trace nogil functions. It is not

‎cuda_core/cuda/core/_cpp/rt/memory.cpp‎

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include <mutex>
1515
#include <stdexcept>
1616
#include <utility>
17-
#include <vector>
1817
#ifndef _WIN32
1918
#include <unistd.h>
2019
#endif
@@ -35,32 +34,42 @@ struct MemoryPoolBox {
3534

3635
// Helper to clear peer access before destroying a memory pool.
3736
// Works around nvbug 5698116: recycled pool handles inherit peer access state.
37+
// The driver validates a request as a whole and rejects it if any entry cannot
38+
// be applied: the pool's own device, a device without memory-map support, or
39+
// devices of more than one kind in one request. Each device is therefore
40+
// revoked with its own request, and the owning device (-1 if the pool has
41+
// none) is not requested at all.
3842
// Must be noexcept since it's called from a shared_ptr deleter.
39-
static void clear_mempool_peer_access(CUmemoryPool pool) noexcept {
43+
static void clear_mempool_peer_access(CUmemoryPool pool, int owner_device) noexcept {
4044
try {
4145
int device_count = 0;
4246
if (p_cuDeviceGetCount(&device_count) != CUDA_SUCCESS || device_count <= 0) {
4347
return;
4448
}
4549

46-
std::vector<CUmemAccessDesc> clear_access(device_count);
50+
CUmemAccessDesc revoke{};
51+
revoke.location.type = CU_MEM_LOCATION_TYPE_DEVICE;
52+
revoke.flags = CU_MEM_ACCESS_FLAGS_PROT_NONE;
4753
for (int i = 0; i < device_count; ++i) {
48-
clear_access[i].location.type = CU_MEM_LOCATION_TYPE_DEVICE;
49-
clear_access[i].location.id = i;
50-
clear_access[i].flags = CU_MEM_ACCESS_FLAGS_PROT_NONE;
54+
if (i == owner_device) {
55+
continue;
56+
}
57+
revoke.location.id = i;
58+
p_cuMemPoolSetAccess(pool, &revoke, 1); // Best effort
5159
}
52-
p_cuMemPoolSetAccess(pool, clear_access.data(), device_count); // Best effort
5360
} catch (...) {
5461
// Swallow exceptions - this is best-effort cleanup in destructor context
5562
}
5663
}
5764

58-
static MemoryPoolHandle wrap_mempool_owned(CUmemoryPool pool) {
65+
// owner_device is the ordinal of a device-located pool, or -1 when there is no
66+
// device to skip (host pools) or the location is unknown (imported pools).
67+
static MemoryPoolHandle wrap_mempool_owned(CUmemoryPool pool, int owner_device) {
5968
auto box = std::shared_ptr<const MemoryPoolBox>(
6069
new MemoryPoolBox{pool},
61-
[](const MemoryPoolBox* b) {
70+
[owner_device](const MemoryPoolBox* b) {
6271
GILReleaseGuard gil;
63-
clear_mempool_peer_access(b->resource);
72+
clear_mempool_peer_access(b->resource, owner_device);
6473
pw_cuMemPoolDestroy(b->resource);
6574
delete b;
6675
}
@@ -74,7 +83,8 @@ MemoryPoolHandle create_mempool_handle(const CUmemPoolProps& props) {
7483
if (CUDA_SUCCESS != (err = p_cuMemPoolCreate(&pool, &props))) {
7584
return {};
7685
}
77-
return wrap_mempool_owned(pool);
86+
int owner_device = props.location.type == CU_MEM_LOCATION_TYPE_DEVICE ? props.location.id : -1;
87+
return wrap_mempool_owned(pool, owner_device);
7888
}
7989

8090
MemoryPoolHandle create_mempool_handle_ref(CUmemoryPool pool) {
@@ -98,7 +108,7 @@ MemoryPoolHandle create_mempool_handle_ipc(int fd, CUmemAllocationHandleType han
98108
if (CUDA_SUCCESS != (err = p_cuMemPoolImportFromShareableHandle(&pool, handle_ptr, handle_type, 0))) {
99109
return {};
100110
}
101-
return wrap_mempool_owned(pool);
111+
return wrap_mempool_owned(pool, -1);
102112
}
103113

104114
// ============================================================================

‎cuda_core/cuda/core/_memory/_virtual_memory_resource.py‎

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -329,19 +329,18 @@ def _grow_allocation_fast_path(
329329
"""
330330
with Transaction() as trans:
331331
# Create new physical memory for the additional size
332-
trans.append(
332+
trans.on_failure(
333333
lambda np=new_ptr, s=aligned_additional_size: raise_if_driver_error(driver.cuMemAddressFree(np, s)[0])
334334
)
335335
res, new_handle = driver.cuMemCreate(aligned_additional_size, prop, 0)
336336
raise_if_driver_error(res)
337-
# Register undo for creation
338-
trans.append(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
337+
trans.on_exit(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
339338

340339
# Map the new physical memory to the extended VA range
341340
(res,) = driver.cuMemMap(new_ptr, aligned_additional_size, 0, new_handle, 0)
342341
raise_if_driver_error(res)
343342
# Register undo for mapping
344-
trans.append(
343+
trans.on_failure(
345344
lambda np=new_ptr, s=aligned_additional_size: raise_if_driver_error(driver.cuMemUnmap(np, s)[0])
346345
)
347346

@@ -393,15 +392,14 @@ def _grow_allocation_slow_path(
393392
res, new_ptr = driver.cuMemAddressReserve(total_aligned_size, addr_align, 0, 0)
394393
raise_if_driver_error(res)
395394
# Register undo for VA reservation
396-
trans.append(
395+
trans.on_failure(
397396
lambda np=new_ptr, s=total_aligned_size: raise_if_driver_error(driver.cuMemAddressFree(np, s)[0])
398397
)
399398

400399
# Get the old allocation handle for remapping
401400
result, old_handle = driver.cuMemRetainAllocationHandle(buf.handle)
402401
raise_if_driver_error(result)
403-
# Register undo for old_handle
404-
trans.append(lambda h=old_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
402+
trans.on_exit(lambda h=old_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
405403

406404
# Unmap the old VA range (aligned previous size)
407405
aligned_prev_size = total_aligned_size - aligned_additional_size
@@ -417,28 +415,26 @@ def _remap_old() -> None:
417415
# TODO: consider logging this exception
418416
pass
419417

420-
trans.append(_remap_old)
418+
trans.on_failure(_remap_old)
421419

422420
# Remap the old physical memory to the new VA range (aligned previous size)
423421
(res,) = driver.cuMemMap(int(new_ptr), aligned_prev_size, 0, old_handle, 0)
424422
raise_if_driver_error(res)
425423

426424
# Register undo for mapping
427-
trans.append(lambda np=new_ptr, s=aligned_prev_size: raise_if_driver_error(driver.cuMemUnmap(np, s)[0]))
425+
trans.on_failure(lambda np=new_ptr, s=aligned_prev_size: raise_if_driver_error(driver.cuMemUnmap(np, s)[0]))
428426

429427
# Create new physical memory for the additional size
430428
res, new_handle = driver.cuMemCreate(aligned_additional_size, prop, 0)
431429
raise_if_driver_error(res)
432-
433-
# Register undo for new physical memory
434-
trans.append(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
430+
trans.on_exit(lambda h=new_handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
435431

436432
# Map the new physical memory to the extended portion (aligned offset)
437433
(res,) = driver.cuMemMap(int(new_ptr) + aligned_prev_size, aligned_additional_size, 0, new_handle, 0)
438434
raise_if_driver_error(res)
439435

440436
# Register undo for mapping
441-
trans.append(
437+
trans.on_failure(
442438
lambda base=int(new_ptr), offs=aligned_prev_size, s=aligned_additional_size: raise_if_driver_error(
443439
driver.cuMemUnmap(base + offs, s)[0]
444440
)
@@ -553,20 +549,20 @@ def allocate(self, size: int, *, stream: Stream | GraphBuilder | None = None) ->
553549
# ---- Create physical memory ----
554550
res, handle = driver.cuMemCreate(aligned_size, prop, 0)
555551
raise_if_driver_error(res)
556-
# Register undo for physical memory
557-
trans.append(lambda h=handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
552+
# Drop the creation reference on either outcome; a successful mapping keeps the allocation alive.
553+
trans.on_exit(lambda h=handle: raise_if_driver_error(driver.cuMemRelease(h)[0]))
558554

559555
# ---- Reserve VA space ----
560556
# Potentially, use a separate size for the VA reservation from the physical allocation size
561557
res, ptr = driver.cuMemAddressReserve(aligned_size, addr_align, config.addr_hint, 0)
562558
raise_if_driver_error(res)
563559
# Register undo for VA reservation
564-
trans.append(lambda p=ptr, s=aligned_size: raise_if_driver_error(driver.cuMemAddressFree(p, s)[0]))
560+
trans.on_failure(lambda p=ptr, s=aligned_size: raise_if_driver_error(driver.cuMemAddressFree(p, s)[0]))
565561

566562
# ---- Map physical memory into VA ----
567563
(res,) = driver.cuMemMap(ptr, aligned_size, 0, handle, 0)
568-
trans.append(lambda p=ptr, s=aligned_size: raise_if_driver_error(driver.cuMemUnmap(p, s)[0]))
569564
raise_if_driver_error(res)
565+
trans.on_failure(lambda p=ptr, s=aligned_size: raise_if_driver_error(driver.cuMemUnmap(p, s)[0]))
570566

571567
# ---- Set access for owner + peers ----
572568
descs = self._build_access_descriptors(prop)
@@ -600,14 +596,11 @@ def deallocate(self, ptr: DevicePointerType, size: int, *, stream: Stream | Grap
600596
from cuda.core._stream import Stream_accept
601597

602598
Stream_accept(stream)
603-
result, handle = driver.cuMemRetainAllocationHandle(ptr)
604-
raise_if_driver_error(result)
599+
# The mapping owns the allocation; unmapping frees its backing memory when no external references remain.
605600
(result,) = driver.cuMemUnmap(ptr, size)
606601
raise_if_driver_error(result)
607602
(result,) = driver.cuMemAddressFree(ptr, size)
608603
raise_if_driver_error(result)
609-
(result,) = driver.cuMemRelease(handle)
610-
raise_if_driver_error(result)
611604

612605
@property
613606
def is_device_accessible(self) -> bool:

‎cuda_core/cuda/core/_program.pxd‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ cdef class Program:
2121
str _code_type # Normalised code_type ("c++", "ptx", "nvvm")
2222
str _pch_status # PCH creation outcome after compile
2323
bytes _nvrtc_name # Source filepath given to NVRTC; a real path for debug builds
24+
bytes _debug_source # Temp file this Program wrote for cuda-gdb, or None
2425
list _extra_options # NVRTC options Program adds on top of ProgramOptions

0 commit comments

Comments
 (0)