macro_rules! custom_global {
($visibility:vis fn $name:ident() -> $G:ty) => { ... };
($visibility:vis type $G:ty) => { ... };
($visibility:vis fn $name:ident() -> struct $G:ident { $($tt:tt)* }) => { ... };
($visibility:vis struct $G:ident { $($tt:tt)* }) => { ... };
}Expand description
On Solana, defines BumpAllocator as the global allocator with given
global state.
When compiling for Solana, defines a new global allocator and a function which returns static object living in mutable memory. The name of the function and type of the global object depend on the invocation.
See also caveats in custom_heap macro.
§Existing type
custom_global!($visibility fn $name() -> $Global);
custom_global!($visibility type $Global);Defines function $name with specified visibility which returns a reference
to a 'static reference to object of type $Global. In the second
invocation, the name of the function is global.
$Global must be a Sync and
bytemuck::Zeroable type. Furthermore, the $name function returns
a shared reference to the static objects which doesn’t allow modification
unless the type has internal mutability. This can be achieved by
Cell.
§Global struct definition
custom_global!($visibility fn $name() -> struct $Global { ... });
custom_global!($visibility struct $Global { ... });Defines a struct $Global and uses that as the global object. Note that
all fields of the struct must be bytemuck::Zeroable however they do
not need to be Sync. When building on Solana, the
macro will unsafely declare $Global as Sync based on the observation
that Solana is single-threaded thus passing data between threads is not
a concern.
Due to Rust technical limitations, to use this form the crate must depend on
bytemuck crate. If it doesn’t, compilation will fail with ‘could not find
bytemuck in the list of imported crates’ error. This can be done by
adding the following to Cargo.toml:
[dependencies.bytemuck]
version = "*"§Non-Solana target
When not building for Solana (i.e. for not(target_os = "solana")
configuration), the macro doesn’t set the global allocator nor defines the
$name function returning the global state. (Note that the invocation with
struct $Global definition defines the struct regardless of the target).
Caller is responsible for using appropriate conditional compilation to provide all the necessary global state. One approach is to provide wrapper functions which use the global object when building on Solana and use static atomic type or locked variables when building on other platforms.
§Example
pub(crate) mod global {
#[cfg(not(feature = "cpi"))]
solana_allocator::custom_global!(struct GlobalData {
counter: Cell<usize>,
});
#[cfg(all(target_os = "solana", not(feature = "cpi")))]
pub fn unique() -> usize {
let counter = &global().counter;
let value = counter.get();
counter.set(value + 1);
value
}
#[cfg(not(target_os = "solana"))]
pub fn unique() -> usize {
use std::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
COUNTER.fetch_add(1, Ordering::SeqCst)
}
}