Skip to content

Commit 30ee8e9

Browse files
authored
Add Bytes::from_owner (#742)
1 parent c45697c commit 30ee8e9

File tree

2 files changed

+326
-3
lines changed

2 files changed

+326
-3
lines changed

src/bytes.rs

+174-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use core::iter::FromIterator;
22
use core::mem::{self, ManuallyDrop};
33
use core::ops::{Deref, RangeBounds};
4+
use core::ptr::NonNull;
45
use core::{cmp, fmt, hash, ptr, slice, usize};
56

67
use alloc::{
@@ -200,6 +201,94 @@ impl Bytes {
200201
}
201202
}
202203

204+
/// Create [Bytes] with a buffer whose lifetime is controlled
205+
/// via an explicit owner.
206+
///
207+
/// A common use case is to zero-copy construct from mapped memory.
208+
///
209+
/// ```
210+
/// # struct File;
211+
/// #
212+
/// # impl File {
213+
/// # pub fn open(_: &str) -> Result<Self, ()> {
214+
/// # Ok(Self)
215+
/// # }
216+
/// # }
217+
/// #
218+
/// # mod memmap2 {
219+
/// # pub struct Mmap;
220+
/// #
221+
/// # impl Mmap {
222+
/// # pub unsafe fn map(_file: &super::File) -> Result<Self, ()> {
223+
/// # Ok(Self)
224+
/// # }
225+
/// # }
226+
/// #
227+
/// # impl AsRef<[u8]> for Mmap {
228+
/// # fn as_ref(&self) -> &[u8] {
229+
/// # b"buf"
230+
/// # }
231+
/// # }
232+
/// # }
233+
/// use bytes::Bytes;
234+
/// use memmap2::Mmap;
235+
///
236+
/// # fn main() -> Result<(), ()> {
237+
/// let file = File::open("upload_bundle.tar.gz")?;
238+
/// let mmap = unsafe { Mmap::map(&file) }?;
239+
/// let b = Bytes::from_owner(mmap);
240+
/// # Ok(())
241+
/// # }
242+
/// ```
243+
///
244+
/// The `owner` will be transferred to the constructed [Bytes] object, which
245+
/// will ensure it is dropped once all remaining clones of the constructed
246+
/// object are dropped. The owner will then be responsible for dropping the
247+
/// specified region of memory as part of its [Drop] implementation.
248+
///
249+
/// Note that converting [Bytes] constructed from an owner into a [BytesMut]
250+
/// will always create a deep copy of the buffer into newly allocated memory.
251+
pub fn from_owner<T>(owner: T) -> Self
252+
where
253+
T: AsRef<[u8]> + Send + 'static,
254+
{
255+
// Safety & Miri:
256+
// The ownership of `owner` is first transferred to the `Owned` wrapper and `Bytes` object.
257+
// This ensures that the owner is pinned in memory, allowing us to call `.as_ref()` safely
258+
// since the lifetime of the owner is controlled by the lifetime of the new `Bytes` object,
259+
// and the lifetime of the resulting borrowed `&[u8]` matches that of the owner.
260+
// Note that this remains safe so long as we only call `.as_ref()` once.
261+
//
262+
// There are some additional special considerations here:
263+
// * We rely on Bytes's Drop impl to clean up memory should `.as_ref()` panic.
264+
// * Setting the `ptr` and `len` on the bytes object last (after moving the owner to
265+
// Bytes) allows Miri checks to pass since it avoids obtaining the `&[u8]` slice
266+
// from a stack-owned Box.
267+
// More details on this: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813375863
268+
// and: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813316032
269+
270+
let owned = Box::into_raw(Box::new(Owned {
271+
lifetime: OwnedLifetime {
272+
ref_cnt: AtomicUsize::new(1),
273+
drop: owned_box_and_drop::<T>,
274+
},
275+
owner,
276+
}));
277+
278+
let mut ret = Bytes {
279+
ptr: NonNull::dangling().as_ptr(),
280+
len: 0,
281+
data: AtomicPtr::new(owned.cast()),
282+
vtable: &OWNED_VTABLE,
283+
};
284+
285+
let buf = unsafe { &*owned }.owner.as_ref();
286+
ret.ptr = buf.as_ptr();
287+
ret.len = buf.len();
288+
289+
ret
290+
}
291+
203292
/// Returns the number of bytes contained in this `Bytes`.
204293
///
205294
/// # Examples
@@ -230,14 +319,16 @@ impl Bytes {
230319
self.len == 0
231320
}
232321

233-
/// Returns true if this is the only reference to the data.
322+
/// Returns true if this is the only reference to the data and
323+
/// `Into<BytesMut>` would avoid cloning the underlying buffer.
234324
///
235-
/// Always returns false if the data is backed by a static slice.
325+
/// Always returns false if the data is backed by a [static slice](Bytes::from_static),
326+
/// or an [owner](Bytes::from_owner).
236327
///
237328
/// The result of this method may be invalidated immediately if another
238329
/// thread clones this value while this is being called. Ensure you have
239330
/// unique access to this value (`&mut Bytes`) first if you need to be
240-
/// certain the result is valid (i.e. for safety reasons)
331+
/// certain the result is valid (i.e. for safety reasons).
241332
/// # Examples
242333
///
243334
/// ```
@@ -536,6 +627,9 @@ impl Bytes {
536627
/// If `self` is not unique for the entire original buffer, this will fail
537628
/// and return self.
538629
///
630+
/// This will also always fail if the buffer was constructed via either
631+
/// [from_owner](Bytes::from_owner) or [from_static](Bytes::from_static).
632+
///
539633
/// # Examples
540634
///
541635
/// ```
@@ -1012,6 +1106,83 @@ unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
10121106
// nothing to drop for &'static [u8]
10131107
}
10141108

1109+
// ===== impl OwnedVtable =====
1110+
1111+
#[repr(C)]
1112+
struct OwnedLifetime {
1113+
ref_cnt: AtomicUsize,
1114+
drop: unsafe fn(*mut ()),
1115+
}
1116+
1117+
#[repr(C)]
1118+
struct Owned<T> {
1119+
lifetime: OwnedLifetime,
1120+
owner: T,
1121+
}
1122+
1123+
unsafe fn owned_box_and_drop<T>(ptr: *mut ()) {
1124+
let b: Box<Owned<T>> = Box::from_raw(ptr as _);
1125+
drop(b);
1126+
}
1127+
1128+
unsafe fn owned_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
1129+
let owned = data.load(Ordering::Relaxed);
1130+
let ref_cnt = &(*owned.cast::<OwnedLifetime>()).ref_cnt;
1131+
let old_cnt = ref_cnt.fetch_add(1, Ordering::Relaxed);
1132+
if old_cnt > usize::MAX >> 1 {
1133+
crate::abort()
1134+
}
1135+
1136+
Bytes {
1137+
ptr,
1138+
len,
1139+
data: AtomicPtr::new(owned as _),
1140+
vtable: &OWNED_VTABLE,
1141+
}
1142+
}
1143+
1144+
unsafe fn owned_to_vec(_data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
1145+
let slice = slice::from_raw_parts(ptr, len);
1146+
slice.to_vec()
1147+
}
1148+
1149+
unsafe fn owned_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
1150+
let bytes_mut = BytesMut::from_vec(owned_to_vec(data, ptr, len));
1151+
owned_drop_impl(data.load(Ordering::Relaxed));
1152+
bytes_mut
1153+
}
1154+
1155+
unsafe fn owned_is_unique(_data: &AtomicPtr<()>) -> bool {
1156+
false
1157+
}
1158+
1159+
unsafe fn owned_drop_impl(owned: *mut ()) {
1160+
let lifetime = owned.cast::<OwnedLifetime>();
1161+
let ref_cnt = &(*lifetime).ref_cnt;
1162+
1163+
let old_cnt = ref_cnt.fetch_sub(1, Ordering::Release);
1164+
if old_cnt != 1 {
1165+
return;
1166+
}
1167+
ref_cnt.load(Ordering::Acquire);
1168+
1169+
let drop_fn = &(*lifetime).drop;
1170+
drop_fn(owned)
1171+
}
1172+
1173+
unsafe fn owned_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
1174+
let owned = data.load(Ordering::Relaxed);
1175+
owned_drop_impl(owned);
1176+
}
1177+
1178+
static OWNED_VTABLE: Vtable = Vtable {
1179+
clone: owned_clone,
1180+
to_vec: owned_to_vec,
1181+
to_mut: owned_to_mut,
1182+
is_unique: owned_is_unique,
1183+
drop: owned_drop,
1184+
};
1185+
10151186
// ===== impl PromotableVtable =====
10161187

10171188
static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable {

tests/test_bytes.rs

+152
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#![warn(rust_2018_idioms)]
22

33
use bytes::{Buf, BufMut, Bytes, BytesMut};
4+
use std::sync::atomic::{AtomicUsize, Ordering};
5+
use std::sync::Arc;
46

7+
use std::panic::{self, AssertUnwindSafe};
58
use std::usize;
69

710
const LONG: &[u8] = b"mary had a little lamb, little lamb, little lamb";
@@ -1479,3 +1482,152 @@ fn split_to_empty_addr_mut() {
14791482
let _ = &empty_start[..];
14801483
let _ = &buf[..];
14811484
}
1485+
1486+
#[derive(Clone)]
1487+
struct SharedAtomicCounter(Arc<AtomicUsize>);
1488+
1489+
impl SharedAtomicCounter {
1490+
pub fn new() -> Self {
1491+
SharedAtomicCounter(Arc::new(AtomicUsize::new(0)))
1492+
}
1493+
1494+
pub fn increment(&self) {
1495+
self.0.fetch_add(1, Ordering::AcqRel);
1496+
}
1497+
1498+
pub fn get(&self) -> usize {
1499+
self.0.load(Ordering::Acquire)
1500+
}
1501+
}
1502+
1503+
#[derive(Clone)]
1504+
struct OwnedTester<const L: usize> {
1505+
buf: [u8; L],
1506+
drop_count: SharedAtomicCounter,
1507+
pub panic_as_ref: bool,
1508+
}
1509+
1510+
impl<const L: usize> OwnedTester<L> {
1511+
fn new(buf: [u8; L], drop_count: SharedAtomicCounter) -> Self {
1512+
Self {
1513+
buf,
1514+
drop_count,
1515+
panic_as_ref: false,
1516+
}
1517+
}
1518+
}
1519+
1520+
impl<const L: usize> AsRef<[u8]> for OwnedTester<L> {
1521+
fn as_ref(&self) -> &[u8] {
1522+
if self.panic_as_ref {
1523+
panic!("test-triggered panic in `AsRef<[u8]> for OwnedTester`");
1524+
}
1525+
self.buf.as_slice()
1526+
}
1527+
}
1528+
1529+
impl<const L: usize> Drop for OwnedTester<L> {
1530+
fn drop(&mut self) {
1531+
self.drop_count.increment();
1532+
}
1533+
}
1534+
1535+
#[test]
1536+
fn owned_is_unique_always_false() {
1537+
let b1 = Bytes::from_owner([1, 2, 3, 4, 5, 6, 7]);
1538+
assert!(!b1.is_unique()); // even if ref_cnt == 1
1539+
let b2 = b1.clone();
1540+
assert!(!b1.is_unique());
1541+
assert!(!b2.is_unique());
1542+
drop(b1);
1543+
assert!(!b2.is_unique()); // even if ref_cnt == 1
1544+
}
1545+
1546+
#[test]
1547+
fn owned_buf_sharing() {
1548+
let buf = [1, 2, 3, 4, 5, 6, 7];
1549+
let b1 = Bytes::from_owner(buf);
1550+
let b2 = b1.clone();
1551+
assert_eq!(&buf[..], &b1[..]);
1552+
assert_eq!(&buf[..], &b2[..]);
1553+
assert_eq!(b1.as_ptr(), b2.as_ptr());
1554+
assert_eq!(b1.len(), b2.len());
1555+
assert_eq!(b1.len(), buf.len());
1556+
}
1557+
1558+
#[test]
1559+
fn owned_buf_slicing() {
1560+
let b1 = Bytes::from_owner(SHORT);
1561+
assert_eq!(SHORT, &b1[..]);
1562+
let b2 = b1.slice(1..(b1.len() - 1));
1563+
assert_eq!(&SHORT[1..(SHORT.len() - 1)], b2);
1564+
assert_eq!(unsafe { SHORT.as_ptr().add(1) }, b2.as_ptr());
1565+
assert_eq!(SHORT.len() - 2, b2.len());
1566+
}
1567+
1568+
#[test]
1569+
fn owned_dropped_exactly_once() {
1570+
let buf: [u8; 5] = [1, 2, 3, 4, 5];
1571+
let drop_counter = SharedAtomicCounter::new();
1572+
let owner = OwnedTester::new(buf, drop_counter.clone());
1573+
let b1 = Bytes::from_owner(owner);
1574+
let b2 = b1.clone();
1575+
assert_eq!(drop_counter.get(), 0);
1576+
drop(b1);
1577+
assert_eq!(drop_counter.get(), 0);
1578+
let b3 = b2.slice(1..b2.len() - 1);
1579+
drop(b2);
1580+
assert_eq!(drop_counter.get(), 0);
1581+
drop(b3);
1582+
assert_eq!(drop_counter.get(), 1);
1583+
}
1584+
1585+
#[test]
1586+
fn owned_to_mut() {
1587+
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1588+
let drop_counter = SharedAtomicCounter::new();
1589+
let owner = OwnedTester::new(buf, drop_counter.clone());
1590+
let b1 = Bytes::from_owner(owner);
1591+
1592+
// Holding an owner will fail converting to a BytesMut,
1593+
// even when the bytes instance has a ref_cnt == 1.
1594+
let b1 = b1.try_into_mut().unwrap_err();
1595+
1596+
// That said, it's still possible, just not cheap.
1597+
let bm1: BytesMut = b1.into();
1598+
let new_buf = &bm1[..];
1599+
assert_eq!(new_buf, &buf[..]);
1600+
1601+
// `.into::<BytesMut>()` has correctly dropped the owner
1602+
assert_eq!(drop_counter.get(), 1);
1603+
}
1604+
1605+
#[test]
1606+
fn owned_to_vec() {
1607+
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1608+
let drop_counter = SharedAtomicCounter::new();
1609+
let owner = OwnedTester::new(buf, drop_counter.clone());
1610+
let b1 = Bytes::from_owner(owner);
1611+
1612+
let v1 = b1.to_vec();
1613+
assert_eq!(&v1[..], &buf[..]);
1614+
assert_eq!(&v1[..], &b1[..]);
1615+
1616+
drop(b1);
1617+
assert_eq!(drop_counter.get(), 1);
1618+
}
1619+
1620+
#[test]
1621+
fn owned_safe_drop_on_as_ref_panic() {
1622+
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1623+
let drop_counter = SharedAtomicCounter::new();
1624+
let mut owner = OwnedTester::new(buf, drop_counter.clone());
1625+
owner.panic_as_ref = true;
1626+
1627+
let result = panic::catch_unwind(AssertUnwindSafe(|| {
1628+
let _ = Bytes::from_owner(owner);
1629+
}));
1630+
1631+
assert!(result.is_err());
1632+
assert_eq!(drop_counter.get(), 1);
1633+
}

0 commit comments

Comments
 (0)