Skip to content

Commit 7b06df9

Browse files
authored
Merge branch 'main' into feat/dlpack-exchange-api
2 parents 32a8db6 + 356f5f3 commit 7b06df9

10 files changed

Lines changed: 1002 additions & 213 deletions

File tree

.github/workflows/tests.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
matrix:
1717
toolchain:
18-
- 1.74
18+
- "1.74"
1919
- stable
2020
- nightly
2121
steps:
@@ -27,6 +27,19 @@ jobs:
2727
- name: Setup sccache-cache
2828
uses: mozilla-actions/sccache-action@v0.0.9
2929

30+
# MSRV backfill: pyo3 0.28 requires rustc >=1.83 and pyo3 0.27 is
31+
# the last line with MSRV 1.74. dlpk accepts `>=0.27, <0.29`, so we
32+
# pin the pyo3 stack to the 0.27 line for the 1.74 job only. Stable
33+
# and nightly resolve to 0.28 naturally.
34+
- name: Pin pyo3 to 0.27 on MSRV toolchain
35+
if: matrix.toolchain == '1.74'
36+
run: |
37+
cargo update -p pyo3 --precise 0.27.2
38+
cargo update -p pyo3-build-config --precise 0.27.2
39+
cargo update -p pyo3-macros --precise 0.27.2
40+
cargo update -p pyo3-macros-backend --precise 0.27.2
41+
cargo update -p pyo3-ffi --precise 0.27.2
42+
3043
- name: Run tests
3144
env:
3245
SCCACHE_GHA_ENABLED: "true"

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "dlpk"
3-
version = "0.1.5"
3+
version = "0.3.1"
44
edition = "2021"
55
rust-version = "1.74"
66
license = "MIT OR Apache-2.0"
@@ -18,7 +18,11 @@ keywords = ["dlpack", "deep-learning", "machine-learning"]
1818

1919
[dependencies]
2020
ndarray = { version = "0.17", optional = true }
21-
pyo3 = { version = "0.26", optional = true }
21+
# Accept either 0.27 or 0.28 so downstream crates on either line can take
22+
# dlpk as a dep without forcing a pyo3 version conflict. 0.28 has MSRV 1.83
23+
# while 0.27 keeps MSRV 1.74; the MSRV CI job uses `cargo update --precise`
24+
# to pin to 0.27.
25+
pyo3 = { version = ">=0.27, <0.29", optional = true }
2226
ouroboros = { version = "0.18", optional = true }
2327

2428
# 2.3 has MSRV 1.7.0

src/data_types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ mod tests {
174174
}
175175

176176
#[test]
177+
#[cfg_attr(miri, ignore)]
177178
fn test_dlpack_pointer_cast() {
178179
let value: u32 = 42;
179180
let mut mock_data = value;

src/lib.rs

Lines changed: 32 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
#![allow(clippy::needless_return, clippy::redundant_field_names)]
2929
#![forbid(clippy::as_ptr_cast_mut, clippy::ptr_cast_constness)]
3030

31-
use std::{ffi::c_void, ptr::NonNull};
31+
use std::ptr::NonNull;
3232

3333
pub mod sys;
3434
pub use self::sys::{DLDataType, DLDataTypeCode, DLDevice, DLDeviceType, DLPackVersion};
@@ -252,63 +252,26 @@ impl Drop for DLPackTensor {
252252
}
253253
}
254254

255-
struct RustBoxedManager {
256-
// tensor: std::pin::Pin<sys::DLManagedTensorVersioned>,
257-
original_ctx: *mut c_void,
258-
original_deleter: Option<unsafe extern "C" fn(*mut sys::DLManagedTensorVersioned) -> ()>,
259-
}
260-
261-
unsafe extern "C" fn rust_boxed_manager_deleter(tensor: *mut sys::DLManagedTensorVersioned) {
262-
if tensor.is_null() {
263-
return;
264-
}
265-
266-
let manager = (*tensor).manager_ctx.cast::<RustBoxedManager>();
267-
assert!(!manager.is_null());
268-
269-
(*tensor).manager_ctx = (*manager).original_ctx;
270-
(*tensor).deleter = (*manager).original_deleter;
271-
272-
if let Some(deleter) = (*tensor).deleter {
273-
deleter(tensor);
274-
}
275-
276-
std::mem::drop(Box::from_raw(manager));
277-
std::mem::drop(Box::from_raw(tensor));
278-
}
279-
280255
impl DLPackTensor {
281-
/// Create a `DLPackTensor` from a raw `DLManagedTensorVersioned`.
256+
/// Create a `DLPackTensor` from a pointer to `DLManagedTensorVersioned`.
282257
///
283258
/// # Safety
284259
///
285260
/// The `DLManagedTensorVersioned` should have a valid `deleter` that can
286261
/// be called from Rust, or have the deleter set to `None`.
287-
pub unsafe fn from_raw(mut tensor: sys::DLManagedTensorVersioned) -> DLPackTensor {
288-
// we need to move the tensor to the heap, so we need to wrap the
289-
// manager_ctx and deleter into another one that will also free the
290-
// tensor from the heap.
291-
let manager = Box::new(RustBoxedManager {
292-
original_ctx: tensor.manager_ctx,
293-
original_deleter: tensor.deleter,
294-
});
295-
tensor.manager_ctx = Box::into_raw(manager).cast();
296-
tensor.deleter = Some(rust_boxed_manager_deleter);
297-
298-
let tensor = Box::new(tensor);
262+
pub unsafe fn from_ptr(tensor: *mut sys::DLManagedTensorVersioned) -> DLPackTensor {
263+
let tensor = NonNull::new(tensor).expect("DLManagedTensorVersioned pointer is null");
299264

300-
return DLPackTensor{
301-
raw: NonNull::new_unchecked(Box::into_raw(tensor)),
302-
};
265+
return DLPackTensor::from_raw(tensor);
303266
}
304267

305-
/// Create a `DLPackTensor` from a non-null pointer to `DLManagedTensorVersioned`.
268+
/// Create a `DLPackTensor` from a non-null pointer to
269+
/// `DLManagedTensorVersioned`.
306270
///
307271
/// # Safety
308272
///
309-
/// The `DLManagedTensorVersioned` should have a valid `deleter` that can
310-
/// be called from Rust, or have the deleter set to `None`.
311-
pub unsafe fn from_ptr(tensor: NonNull<sys::DLManagedTensorVersioned>) -> DLPackTensor {
273+
/// The same safety requirements as `from_ptr` apply.
274+
pub unsafe fn from_raw(tensor: NonNull<sys::DLManagedTensorVersioned>) -> DLPackTensor {
312275
if tensor.as_ref().version.major != sys::DLPACK_MAJOR_VERSION {
313276
// from the spec, we need to call the deleter here (and it is the
314277
// only thing we can do)
@@ -353,6 +316,14 @@ impl DLPackTensor {
353316
}
354317
}
355318

319+
/// Get the ABI version of this DLPack tensor.
320+
pub fn version(&self) -> DLPackVersion {
321+
let tensor_ref = unsafe {
322+
self.raw.as_ref()
323+
};
324+
tensor_ref.version
325+
}
326+
356327
/// Get a pointer to data in this tensor. This pointer can be a device
357328
/// pointer according to [`DLPackTensor::device`].
358329
pub fn data_ptr<T>(&self) -> Result<*const T, CastError> where T: DLPackPointerCast {
@@ -504,9 +475,13 @@ impl<'a> DLPackTensorRef<'a> {
504475

505476
/// Get the shape of this tensor
506477
pub fn shape(&self) -> &[i64] {
507-
assert!(!self.raw.shape.is_null());
508-
unsafe {
509-
return std::slice::from_raw_parts(self.raw.shape, self.n_dims());
478+
if self.raw.shape.is_null() {
479+
assert!(self.raw.ndim == 0, "Shape pointer is null but ndim is not 0");
480+
return &[];
481+
} else {
482+
unsafe {
483+
return std::slice::from_raw_parts(self.raw.shape, self.n_dims());
484+
}
510485
}
511486
}
512487

@@ -639,6 +614,14 @@ pub mod pyo3;
639614
#[cfg(feature = "sync")]
640615
pub mod sync;
641616

617+
/// Small wrapper type to mark a tensor as read-only when there is a choice
618+
/// between a read-only and a read-write implementation.
619+
pub struct ReadOnly<T>(pub T);
620+
621+
/// Small wrapper type to mark a tensor as read-write when there is a choice
622+
/// between a read-only and a read-write implementation.
623+
pub struct ReadWrite<T>(pub T);
624+
642625

643626
#[cfg(test)]
644627
mod tests {

0 commit comments

Comments
 (0)