Mostly Typed

For Hackers and Other Heretics

Cannot Declare Drop Types Statically

So far in Tock we have been allocating the chip and platform definitions statically by declaring them in static mutable Option variables. They are declared as None but on platform initialization, we replace their values with real values:

static mut CHIP : Option<sam4l::Sam4l> = None;
static mut FIRESTORM : Option<Firestorm> = None;

pub unsafe fn init() -> &'static Firestorm {
    CHIP = Some(sam4l::Sam4l::new());
    ...
    FIRESTORM = Some(Firestorm {
      ...
    });
    ...
    FIRESTORM.as_mut().unwrap()
}

This works fine as long as the data structures representing chip and platform adhere to the restrictions imposted on statically declared variables. Specifically, Rust forbids statics from containing types with destructors (e.g. types that implement the Drop trait). This is motivated by the desire to avoid executing code before/after main() while not leaking destructors. There is some open discussion to relax this constraint, but it seems unlikely to change soon.

Unfortunately, Tock platforms will contain drivers (obviously), which in turn will need to have references to things with destructors — specifically memory allocated dynamically from application memory, which needs to be freed when it goes out of scope. For example, the Console driver looks like this:

struct Console<U: UART + 'static> {
    ...
    read_buffer: Option<AppPtr<[u8; 40]>>,
    ...
}

AppPtr is a pointer to memory allocated by the driver within the application’s memory region (and hidden from the application). It acts like Rust’s standard library’s Box type in that the underlying memory is implicitly freed when the value goes out of scope, using the Drop trait.

What this means is that we cannot declare the platform variable statically as we had been as it now contains a type with desctructors. The concern for Rust in general may be valid, but we don’t care about leaking destructors. The working solution for now is to declare buffers of the approriate size instead of the types themselves and allocate unsafely into those buffers:

static mut FIRESTORM_BUF : [u8; 152] = [0; 152];
let _ : Firestorm = mem::transmute(FIRESTORM_BUF);
let firestorm : &'static mut Firestorm =
    mem::transmute(&mut FIRESTORM_BUF);
*firestorm = Firestorm { ... };

There are two let bindins in the code above. The first is used to ensure that the buffer is sized appropriately (transmute will throw a compile time error if the source and destination types are not of the same size), and the second is casts the buffer to a reference, which is what we actually use.

This ends up being annoying since any changes to the type require a manual change to the size of the buffer. However, const fns how now landed in Rust nightly, and we should expect size_of to be declared a const fn soon, which will allow us to declare buffers like this:

static mut FIRESTORM_BUF : [u8; size_of<Firestorm>()] =
    [0; size_of<Firestorm>()];