Skip to content

Commit 5c07c28

Browse files
committed
check_reservation_size
1 parent f89425a commit 5c07c28

4 files changed

Lines changed: 61 additions & 0 deletions

File tree

python/rapidsmpf/rapidsmpf/memory/buffer_resource.pyx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ from libcpp.vector cimport vector
1414

1515
from rmm.pylibrmm import CudaStreamFlags
1616

17+
from rapidsmpf.utils.memory import check_reservation_size
18+
1719
from rmm.librmm.memory_resource cimport (any_resource, device_accessible,
1820
device_async_resource_ref,
1921
make_any_device_resource)
@@ -416,6 +418,7 @@ cdef class BufferResource:
416418
- On failure, the reservation's size equals zero (a zero-sized reservation
417419
never fails).
418420
"""
421+
check_reservation_size(size)
419422
cdef pair[unique_ptr[cpp_MemoryReservation], size_t] ret
420423
with nogil:
421424
ret = cpp_br_reserve(self._handle, mem_type, size, allow_overbooking)
@@ -451,6 +454,7 @@ cdef class BufferResource:
451454
If overbooking is disabled and the buffer resource cannot free enough
452455
device memory through spilling to satisfy the request.
453456
"""
457+
check_reservation_size(size)
454458
cdef unique_ptr[cpp_MemoryReservation] ret
455459
with nogil:
456460
ret = cpp_br_reserve_device_memory_and_spill(
@@ -485,6 +489,7 @@ cdef class BufferResource:
485489
RuntimeError
486490
If no memory type in ``mem_types`` could satisfy the reservation.
487491
"""
492+
check_reservation_size(size)
488493
cdef vector[MemoryType] cpp_mem_types
489494
for mt in mem_types:
490495
cpp_mem_types.push_back(<MemoryType?>mt)

python/rapidsmpf/rapidsmpf/streaming/core/memory_reserve_or_wait.pyx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ from rapidsmpf.streaming.core.context cimport Context, cpp_Context
2222
import asyncio
2323

2424
import rapidsmpf.utils.string
25+
from rapidsmpf.utils.memory import check_reservation_size
2526

2627
# Sentinel indicating that net_memory_delta estimation has not yet been implemented.
2728
#
@@ -394,6 +395,7 @@ cdef class MemoryReserveOrWait:
394395
RuntimeError
395396
If shutdown occurs before the request can be processed.
396397
"""
398+
check_reservation_size(size)
397399
cdef shared_ptr[unique_ptr[cpp_MemoryReservation]] c_ret
398400
future = asyncio.get_running_loop().create_future()
399401
Py_INCREF(future)
@@ -444,6 +446,7 @@ cdef class MemoryReserveOrWait:
444446
RuntimeError
445447
If shutdown occurs before the request can be processed.
446448
"""
449+
check_reservation_size(size)
447450
cdef shared_ptr[pair[unique_ptr[cpp_MemoryReservation], size_t]] c_ret
448451
future = asyncio.get_running_loop().create_future()
449452
Py_INCREF(future)
@@ -497,6 +500,7 @@ cdef class MemoryReserveOrWait:
497500
--------
498501
reserve_or_wait
499502
"""
503+
check_reservation_size(size)
500504
cdef shared_ptr[unique_ptr[cpp_MemoryReservation]] c_ret
501505
future = asyncio.get_running_loop().create_future()
502506
Py_INCREF(future)
@@ -602,6 +606,8 @@ async def reserve_memory(
602606
... allow_overbooking=False,
603607
... )
604608
"""
609+
check_reservation_size(size)
610+
605611
if allow_overbooking is None:
606612
allow_overbooking = ctx.options().get(
607613
"allow_overbooking_by_default",

python/rapidsmpf/rapidsmpf/tests/test_buffer_resource.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from rapidsmpf.memory.buffer_resource import BufferResource, OwningDeviceMemoryResource
1414
from rapidsmpf.memory.memory_reservation import opaque_memory_usage
1515
from rapidsmpf.statistics import Statistics
16+
from rapidsmpf.utils.memory import _MAX_RESERVATION_BYTES
1617

1718

1819
def KiB(x: int) -> int:
@@ -76,6 +77,18 @@ def test_memory_reservation(mem_type: MemoryType) -> None:
7677
br.release(res1, KiB(10))
7778

7879

80+
@pytest.mark.parametrize("mem_type", [MemoryType.DEVICE, MemoryType.HOST])
81+
def test_reserve_rejects_size_underflow(mem_type: MemoryType) -> None:
82+
mr = rmm.mr.CudaMemoryResource()
83+
br = BufferResource(mr, memory_limits={mem_type: KiB(100)})
84+
85+
with pytest.raises(ValueError, match="underflow"):
86+
br.reserve(mem_type, _MAX_RESERVATION_BYTES + 1, allow_overbooking=True)
87+
88+
res, _ = br.reserve(mem_type, _MAX_RESERVATION_BYTES, allow_overbooking=True)
89+
assert res.size == _MAX_RESERVATION_BYTES
90+
91+
7992
def test_stream_pool() -> None:
8093
"""Test that stream_pool parameter can be configured."""
8194
mr = rmm.mr.CudaMemoryResource()
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Memory utilities."""
4+
5+
from __future__ import annotations
6+
7+
# Largest reservation size addressable as a signed 64-bit byte count (INT64_MAX).
8+
_MAX_RESERVATION_BYTES = (1 << 63) - 1
9+
10+
11+
def check_reservation_size(size: int) -> None:
12+
"""
13+
Reject a reservation size that cannot represent a real allocation.
14+
15+
A value above ``INT64_MAX`` almost always means the caller computed ``size``
16+
using an unsigned subtraction that underflowed and wrapped around outside of
17+
Python. Performing this check in Python, before the value is coerced into a
18+
``size_t``, lets us raise a traceback that points at the offending caller
19+
rather than silently passing the wrapped value down to C++ where it only
20+
surfaces later as an opaque "value out of range" cast error.
21+
22+
Parameters
23+
----------
24+
size
25+
Requested reservation size in bytes.
26+
27+
Raises
28+
------
29+
ValueError
30+
If ``size`` exceeds the addressable range.
31+
"""
32+
if size > _MAX_RESERVATION_BYTES:
33+
raise ValueError(
34+
f"reservation size ({size}) exceeds the addressable range; this almost "
35+
f"certainly indicates an unsigned (size_t) underflow in the caller's size "
36+
"computation"
37+
)

0 commit comments

Comments
 (0)