Attribute Macro static_init::dynamic

source ·
#[dynamic]
Expand description

Declare statics that can be initialized with non const fonctions and safe mutable statics

Statics on which this attribute is applied will be be initialized at run time (optionaly see bellow), before main start. This allow statics initialization with non const expressions.

There are two main categories of statics:

  • lazy statics which are statics that are always safe to use. They may be initialized on first acces or before main is called;

  • locked lazy statics which are the mutable version of lazy statics.

  • raw statics, which are initialized at program start-up and absolutely unchecked. Any access to them requires unsafe block;

§Lazy statics

Those statics are initialized on first access. An optimization implemented by lesser lazy statics initialize the static before main is called on all tier1 plateform but mach.

The declared object is encapsulated in a type that implement Deref.

Other access functionnality and state information are accessible through the LazyAccess trait.

Those lazy can be used with regular statics and thread locals.


#[dynamic]
static A :Vec<i32> = vec![1,2];

#[dynamic]
#[thread_local]
static TL :Vec<i32> = vec![1,2];

§Lesser Lazy Statics

They are declared with the #[dynamic] attribute (or equivalently #[dynamic(lesser_lazy)]. They are either initialized on first access or before main is called. They provide unsurpassable access performance: their access time is comparable to const initialized statics but they support non const initialization:

#[dynamic]
static V :Vec<i32> = vec![1,2];

assert_eq!(V.len(), 2);

§Realy lazy Statics

When initialization on first access is a requirement, the static shall be attributed with #[dynamic(lazy)]

#[dynamic(lazy)]
static V :Vec<i32> = vec![1,2];

assert_eq!(*V, vec![1,2]);

§Finalized statics

The attribute argument finalize can be used if the declared type of the static implement Finaly trait. The finalize method is called at program exit or at thread exit for thread locals. (NB: mutable lazy also support drop, see below)



struct A(i32);

impl Finaly for A {
    fn finaly(&self){/* some clean up code */ }
}

#[dynamic(finalize)] //finalize execute at program exit
static X :A = A(33);

#[dynamic(lazy,finalize)] //finalize executed at thread exit
#[thread_local]
static Y :A = A(33);

§Tolerances

§Initialization fault tolerance

By default if the initialization of a lazy panic, initialization will be attempted once again on the next access attempt. If this is not desired the lazy should be declared with attribute argument try_init_once, in which case, the lazy will be poisonned if initialization panics.

#[dynamic(try_init_once)]
static X :Vec<i32> = vec![1,2];

#[dynamic(lazy,try_init_once)] //attribute argument can be combined
static Y :Vec<i32> = vec![1,2];

§Registration for finalization tolerance

By default lazy that intended to be finalized (because they use the finalize or drop attribute argument) refuse to initialize if registration of the finalization or drop at program exit or thread exit fails.

If this is not desired, the tolerate_leak attribute argument can be used.

struct A(i32);

impl Finaly for A {
    fn finaly(&self){/* some clean up code */ }
}

#[dynamic(finalize,tolerate_leak)]
static X :A = A(21);
//the initialization may succeed even if it is impossible to register
//the call to finaly at program exit

§Locked lazy statics

Those statics are mutable statics, initialized on the first acces and protected behind a kind of read/write lock specialy designed for them.

The are declared as lazy statics but with the mut keyword. The macro will actualy remove the mut keyword and use a r/w locked wrapper type:


#[dynamic]
static mut V: Vec<i32> = vec![1,2];

V.write().push(3);

assert_eq!(*V.read(), vec![1,2,3]);

Those statics provides different methods to access the target object. See the documentation of LockedLazy for exemple. All locked lazy types provide the same methods.

Locked lazy statics support all attribute arguments supported by lazy statics: finalize, try_init_once, tolerate_leak. Moreover they support two other arguments:

  • drop in which case the static will be dropped at program exit:
  • prime which is a static that support access before it is actualy initialized and after it is droped;

§Dropped locked lazy statics

Locked lazy statics can be droped at program exit or thread exit when declared with the drop attribute argument




#[dynamic(drop)]
#[thread_local]
static mut V: Vec<i32> = vec![1,2];

#[dynamic(lazy,drop,tolerate_leak)]
static mut V2: Vec<i32> = vec![1,2];

§Primed locked lazy statics

Those statics model the case where an object should have a standard behavior and a fallback behavior after ressources are release or not yet acquired.

Those statics are initialized in two steps:

  • a const initialization that happens at compile time

  • a dynamic intialization that happens the first time they are accessed if if is declared with lazy attribute argument or just before.

More over they are conceptualy uninitialized if the type of the statics implement the Uninit trait and is declared with the drop attribute argument.

They must be initialized with a match expression as exemplified bellow:

use static_init::{dynamic, Uninit};

#[dynamic(prime)]
static mut O: Option<Vec<i32>> = match INIT {
    PRIME => None,
    DYN => Some(vec![1,2]),
    };

#[dynamic(lazy,prime)]
static mut OLAZY: Option<Vec<i32>> = match INIT {
    PRIME => None,
    DYN => Some(vec![1,2]),
    };


struct A(Option<Vec<i32>>);

impl Uninit for A {
    fn uninit(&mut self) {
        self.0.take();
    }
}

#[dynamic(prime,finalize)]//finalize/drop actualy means uninit for primed lazy
static mut P: A = match INIT {
    PRIME => A(None),
    DYN => A(Some(vec![1,2])),
    };

match P.primed_read() {
    Ok(read_lock) => (),/*a read lock that refers to the initialized statics */
    Err(read_lock) => (),/* post finalization access, uninit has already been called*/
    }

match P.primed_write() {
    Ok(write_lock) => (),/*a write lock that refers to the initialized statics */
    Err(read_lock) => (),/* post finalization access, uninit has already been called*/
    }

§Raw statics

Those statics will be initialized at program startup, without ordering, accept between those that have different priorities on plateform that support priorities. Those statics are supported on unixes and windows with priorities and mac without priorities.

§Safety

During initialization, any access to other “dynamic” statics initialized with a lower priority will cause undefined behavior. Similarly, during drop any access to a “dynamic” static dropped with a lower priority will cause undefined behavior. For this reason those statics are always turn into mutable statics to ensure that all access attempt is unsafe.

Those statics are interesting only to get the optimalest performance at the price of unsafety.

#[dynamic(0)]
static V :Vec<i32> = vec![1,2];

assert!(unsafe{*V == vec![1,2]})

§Execution Order

The execution order of raw static initializations is unspecified. Nevertheless on ELF plateform (linux,any unixes but mac) and windows plateform a priority can be specified using the syntax dynamic(<num>) where <num> is a number included in the range [0 ; 216-1].

Statics with priority number 65535 are initialized first (in unspecified order), then statics with priority number 65534 are initialized … then statics with priority number 0.

//V1 must be initialized first
//because V2 uses the value of V1.

#[dynamic(20)]
static mut V1 :Vec<i32> = vec![1,2];

#[dynamic(10)]
static V2 :Vec<i32> = unsafe{V1.push(3); V1.clone()};

§Drop

Those statics can use the drop attribute argument. In this case the static will be droped at program exit

#[dynamic(0, drop)]
static mut V1 :Vec<i32> = vec![1,2];

The drop priority can be specified with the drop=<priority> syntax. If no priority is given, the drop priority will equal the one of the initialization priority.


#[dynamic(10, drop)] //equivalent to #[dynamic(10,drop=10)]
//or longer #[dynamic(init=10,drop=10)]
static mut V1 :Vec<i32> = vec![1,2];

#[dynamic(42, drop=33)]
static mut V2 :Vec<i32> = vec![1,2];

The drop priorities are sequenced in the reverse order of initialization priority. The smaller is the priority the sooner is droped the static.

Finaly the drop_only=<priority> is equivalent to #[dynamic(0,drop=<priority>)] except that the static will be const initialized.

struct A;
impl Drop for A {
  fn drop(&mut self) {}
  }

#[dynamic(drop_only=33)]
static V2: A = A;