custom_global

Macro custom_global 

Source
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)
    }
}