japaric-archived / photon-quickstart

Cargo template for developing photon applications
Apache License 2.0
43 stars 5 forks source link

memory safe data sharing #13

Closed japaric closed 7 years ago

japaric commented 7 years ago

this commit allow lock-less memory safe data sharing between the "main" context, the setup / loop functions, and cloud functions through the Resource abstraction.

Resources are global variables that are safe to access from different contexts. Safety is enforced in two ways:

Single context memory safety: The Resource.access{,_mut} methods take a "context" token, App or Cloud, to preserve Rust borrowing rules: only one mutable reference (&mut -) OR several shared references (&-) may exist at any given time. This rule prevents pointer invalidation. This is the kind of problem we prevent at compile time:

fn loop_(ref mut app: App) {
    static OWNED: Resource<Option<i32>> = Resource::new(Some(0));

    let owned = OWNED.access(app);
    let shared_ref: &i32 = owned.as_ref().unwrap();
    let mut_ref = OWNED.access_mut(app);
    // ~^ compile error (`app` has been frozen by the `shared_ref` borrow)

    // `OWNED` changed to `None`. `shared_ref` has been invalidated
    *mut_ref.as_mut().unwrap() = None;

    let bad = *shared_ref;
}

Memory safety during preemption: If SYSTEM_THREAD is not enabled, which is our case, then cloud functions can only preempt the main context, loop / setup, when the App.delay_ms method is called. To ensure no pointer invalidation occurs in that case a App.delay_ms call won't compile if there are outstanding borrows to resources in the current context. This is the kind of problem we prevent at compile time:

static SHARED: Resource<Option<i32>> = Resource::new(Some(0));

fn loop_(ref mut app: App) {
    let shared = SHARED.access(app);
    let shared_ref: &i32 = shared.as_ref().unwrap();

    // `task` preempts this context during the delay
    app.delay_ms(100);
    //~^ compile error (`app` has been frozen by the `shared_ref` borrow)

    // `shared_ref` would have been invalidated at this point because `SHARED`
    // now contains a `None` variant
    let bad = *shared_ref;
}

fn task(_: String, ref mut cloud: Cloud) {
    // "empties" the SHARED resource
    *SHARED.access_mut(cloud) = None;
}

cc @dbrgn the function and variable examples are now fully safe

japaric commented 7 years ago

@homunkulus r+

homunkulus commented 7 years ago

:pushpin: Commit 69a7ffd has been approved by japaric

homunkulus commented 7 years ago

:hourglass: Testing commit 69a7ffd with merge 69a7ffd...

homunkulus commented 7 years ago

:broken_heart: Test failed - status-travis

japaric commented 7 years ago

Error: Download failed on Cask 'gcc-arm-embedded' with message: Download failed: https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update/+download/gcc-arm-none-eabi-5_4-2016q3-20160926-mac.tar.bz2

Network error

@homunkulus retry

japaric commented 7 years ago

@homunkulus r+

homunkulus commented 7 years ago

:pushpin: Commit ddbfd92 has been approved by japaric

homunkulus commented 7 years ago

:hourglass: Testing commit ddbfd92 with merge ddbfd92...

homunkulus commented 7 years ago

:sunny: Test successful - status-travis Approved by: japaric Pushing ddbfd92b6b487ca5f90fd859a62c61c6bbe04b1a to master...

dbrgn commented 7 years ago

Awesome!