Skip to content
This repository has been archived by the owner on Dec 9, 2018. It is now read-only.

memory safe data sharing #13

Merged
merged 1 commit into from
May 17, 2017
Merged

memory safe data sharing #13

merged 1 commit into from
May 17, 2017

Conversation

japaric
Copy link
Contributor

@japaric japaric commented May 17, 2017

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
Copy link
Contributor Author

japaric commented May 17, 2017

@homunkulus r+

@homunkulus
Copy link
Collaborator

📌 Commit 69a7ffd has been approved by japaric

@homunkulus
Copy link
Collaborator

⌛ Testing commit 69a7ffd with merge 69a7ffd...

@homunkulus
Copy link
Collaborator

💔 Test failed - status-travis

@japaric
Copy link
Contributor Author

japaric commented May 17, 2017

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

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

`Resource`s 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 inked 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:

``` rust
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 this 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;
}
```
@japaric
Copy link
Contributor Author

japaric commented May 17, 2017

@homunkulus r+

@homunkulus
Copy link
Collaborator

📌 Commit ddbfd92 has been approved by japaric

@homunkulus
Copy link
Collaborator

⌛ Testing commit ddbfd92 with merge ddbfd92...

@homunkulus
Copy link
Collaborator

☀️ Test successful - status-travis
Approved by: japaric
Pushing ddbfd92 to master...

@homunkulus homunkulus merged commit ddbfd92 into master May 17, 2017
@japaric japaric deleted the safe branch May 17, 2017 01:23
@dbrgn
Copy link
Contributor

dbrgn commented May 17, 2017

Awesome!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants