Skip to content

Commit

Permalink
Use a 64K static for zeroed scratch space in upb
Browse files Browse the repository at this point in the history
The actual construction of the zeroed block is now entirely safe.
This will be accessed in nearly every program using protobuf.
Using a static in .bss has much less overhead than an atomically-constructed
dynamic allocation and is far more predictable for space-constrained systems.

In the future, if dynamic allocation is kept, it should use std::sync::OnceLock
instead of the much less safe Once combined with `static mut`.

PiperOrigin-RevId: 609635555
  • Loading branch information
kupiakos authored and copybara-github committed Feb 23, 2024
1 parent 8afaae5 commit 4395d97
Showing 1 changed file with 11 additions and 23 deletions.
34 changes: 11 additions & 23 deletions rust/upb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ use std::cell::UnsafeCell;
use std::ffi::c_int;
use std::fmt;
use std::marker::PhantomData;
use std::mem::{size_of, ManuallyDrop, MaybeUninit};
use std::mem::{align_of, size_of, ManuallyDrop, MaybeUninit};
use std::ops::Deref;
use std::ptr::{self, NonNull};
use std::slice;
use std::sync::{Once, OnceLock};
use std::sync::OnceLock;

/// See `upb/port/def.inc`.
const UPB_MALLOC_ALIGN: usize = 8;
const _CHECK_UPB_MALLOC_ALIGN_AT_LEAST_POINTER_ALIGNED: () =
assert!(UPB_MALLOC_ALIGN >= align_of::<*const ()>());

/// A wrapper over a `upb_Arena`.
///
Expand Down Expand Up @@ -148,37 +150,23 @@ impl Drop for Arena {
}
}

static mut INTERNAL_PTR: Option<RawMessage> = None;
static INIT: Once = Once::new();

// The scratch size of 64 KiB matches the maximum supported size that a
// upb_Message can possibly be.
// TODO: Allow dynamic sized ScratchSpace.
/// The scratch size of 64 KiB matches the maximum supported size that a
/// upb_Message can possibly be.
const UPB_SCRATCH_SPACE_BYTES: usize = 65_536;
const ALIGN: usize = 32;

/// Holds a zero-initialized block of memory for use by upb.
///
/// By default, if a message is not set in cpp, a default message is created.
/// upb departs from this and returns a null ptr. However, since contiguous
/// chunks of memory filled with zeroes are legit messages from upb's point of
/// view, we can allocate a large block and refer to that when dealing
/// with readonly access.
pub struct ScratchSpace;
#[repr(C, align(8))] // align to UPB_MALLOC_ALIGN = 8
pub struct ScratchSpace([u8; UPB_SCRATCH_SPACE_BYTES]);
impl ScratchSpace {
pub fn zeroed_block(_private: Private) -> RawMessage {
unsafe {
INIT.call_once(|| {
let layout =
std::alloc::Layout::from_size_align(UPB_SCRATCH_SPACE_BYTES, ALIGN).unwrap();
let Some(ptr) =
crate::__internal::RawMessage::new(std::alloc::alloc_zeroed(layout).cast())
else {
std::alloc::handle_alloc_error(layout)
};
INTERNAL_PTR = Some(ptr)
});
INTERNAL_PTR.unwrap()
}
static ZEROED_BLOCK: ScratchSpace = ScratchSpace([0; UPB_SCRATCH_SPACE_BYTES]);
NonNull::from(&ZEROED_BLOCK).cast()
}
}

Expand Down

0 comments on commit 4395d97

Please sign in to comment.