Skip to content

Commit

Permalink
actor API
Browse files Browse the repository at this point in the history
see rtic-rs/rfcs#52 for details
includes: core proposal and `#[init]` and `memory-watermark` extensions

Co-authored-by: Jonas Schievink <[email protected]>
  • Loading branch information
japaric and jonas-schievink committed Sep 27, 2021
1 parent 58a4920 commit 00f09c8
Show file tree
Hide file tree
Showing 27 changed files with 1,098 additions and 44 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ name = "rtic"
[dependencies]
cortex-m = "0.7.0"
cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" }
rtic-actor-traits = { path = "actor-traits" }
rtic-monotonic = "0.1.0-alpha.2"
rtic-core = "0.3.1"
heapless = "0.7.7"
Expand All @@ -42,13 +43,18 @@ version = "0.5.2"
[target.x86_64-unknown-linux-gnu.dev-dependencies]
trybuild = "1"

[features]
memory-watermark = ["cortex-m-rtic-macros/memory-watermark"]

[profile.release]
codegen-units = 1
lto = true

[workspace]
members = [
"actor-traits",
"macros",
"post-spy",
"xtask",
]

Expand Down
6 changes: 6 additions & 0 deletions actor-traits/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
edition = "2018"
name = "rtic-actor-traits"
version = "0.1.0"

[dependencies]
9 changes: 9 additions & 0 deletions actor-traits/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![no_std]

pub trait Post<M> {
fn post(&mut self, message: M) -> Result<(), M>;
}

pub trait Receive<M> {
fn receive(&mut self, message: M);
}
60 changes: 60 additions & 0 deletions examples/actor-capacity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![no_main]
#![no_std]

use panic_semihosting as _;

#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])]
mod app {
use core::sync::atomic::{AtomicU8, Ordering};

use cortex_m_semihosting::{debug, hprintln};
use rtic_actor_traits::Receive;

struct Actor;

struct Message;

static CALL_COUNT: AtomicU8 = AtomicU8::new(0);

impl Receive<Message> for Actor {
fn receive(&mut self, _: Message) {
hprintln!("Actor::receive was called").ok();
CALL_COUNT.store(CALL_COUNT.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
}
}

#[actors]
struct Actors {
#[subscribe(Message, capacity = 2)]
actor: Actor,
}

#[init]
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
assert!(cx.poster.post(Message).is_ok());
assert!(cx.poster.post(Message).is_ok());
assert!(cx.poster.post(Message).is_err());

(
Shared {},
Local {},
init::Monotonics(),
Actors { actor: Actor },
)
}

#[idle]
fn idle(_: idle::Context) -> ! {
assert_eq!(2, CALL_COUNT.load(Ordering::Relaxed));

loop {
debug::exit(debug::EXIT_SUCCESS);
}
}

#[shared]
struct Shared {}

#[local]
struct Local {}
}
47 changes: 47 additions & 0 deletions examples/actor-init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#![no_main]
#![no_std]

use panic_semihosting as _;

#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])]
mod app {
use cortex_m_semihosting::{debug, hprintln};
use rtic_actor_traits::Receive;

#[derive(Debug)]
struct Actor {
state: i32,
}

struct AssertActorWasInitialized;

const INITIAL_STATE: i32 = 42;

impl Receive<AssertActorWasInitialized> for Actor {
fn receive(&mut self, _: AssertActorWasInitialized) {
assert_eq!(INITIAL_STATE, self.state);
hprintln!("OK").ok();
debug::exit(debug::EXIT_SUCCESS);
}
}

#[actors]
struct Actors {
#[subscribe(AssertActorWasInitialized)]
#[init(Actor { state: INITIAL_STATE })]
actor: Actor,
}

#[init]
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
cx.poster.post(AssertActorWasInitialized).ok();

(Shared {}, Local {}, init::Monotonics(), Actors {})
}

#[shared]
struct Shared {}

#[local]
struct Local {}
}
69 changes: 69 additions & 0 deletions examples/actor-post.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#![no_main]
#![no_std]

use panic_semihosting as _;

#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])]
mod app {
use core::sync::atomic::{AtomicBool, Ordering};

use cortex_m_semihosting::{debug, hprintln};
use rtic_actor_traits::Receive;

struct Actor;

const PAYLOAD: i32 = 42;

struct Message {
payload: i32,
}

static RECEIVE_WAS_CALLED: AtomicBool = AtomicBool::new(false);

impl Receive<Message> for Actor {
fn receive(&mut self, m: Message) {
hprintln!("Actor::receive was called").ok();

RECEIVE_WAS_CALLED.store(true, Ordering::Relaxed);

assert_eq!(PAYLOAD, m.payload);
}
}

#[actors]
struct Actors {
#[subscribe(Message)]
actor: Actor,
}

#[init]
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
cx.poster.post(Message { payload: PAYLOAD }).ok();

// receive invocation withheld
assert!(!RECEIVE_WAS_CALLED.load(Ordering::Relaxed));

(
Shared {},
Local {},
init::Monotonics(),
Actors { actor: Actor },
)
}

#[idle]
fn idle(_: idle::Context) -> ! {
// receive invocation must have executed by now
assert!(RECEIVE_WAS_CALLED.load(Ordering::Relaxed));

loop {
debug::exit(debug::EXIT_SUCCESS);
}
}

#[shared]
struct Shared {}

#[local]
struct Local {}
}
87 changes: 87 additions & 0 deletions examples/actor-publish-failure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#![no_main]
#![no_std]

use panic_semihosting as _;

#[rtic::app(device = lm3s6965, dispatchers = [GPIOA, GPIOB])]
mod app {
use core::sync::atomic::{AtomicBool, Ordering};

use cortex_m_semihosting::{debug, hprintln};
use rtic_actor_traits::Receive;

struct A;
struct B;

#[derive(Default)]
struct M {
must_not_be_cloned: bool,
}

impl Clone for M {
fn clone(&self) -> Self {
assert!(!self.must_not_be_cloned);
M {
must_not_be_cloned: self.must_not_be_cloned,
}
}
}

impl Receive<M> for A {
fn receive(&mut self, _: M) {
static WAS_CALLED_EXACTLY_ONCE: AtomicBool = AtomicBool::new(false);
hprintln!("A::receive was called").ok();
assert!(!WAS_CALLED_EXACTLY_ONCE.load(Ordering::Relaxed));
WAS_CALLED_EXACTLY_ONCE.store(true, Ordering::Relaxed);
}
}

impl Receive<M> for B {
fn receive(&mut self, _: M) {
static WAS_CALLED_EXACTLY_ONCE: AtomicBool = AtomicBool::new(false);
hprintln!("B::receive was called").ok();
assert!(!WAS_CALLED_EXACTLY_ONCE.load(Ordering::Relaxed));
WAS_CALLED_EXACTLY_ONCE.store(true, Ordering::Relaxed);
}
}

#[actors]
struct Actors {
#[subscribe(M, capacity = 2)]
#[init(A)]
a: A,

#[subscribe(M, capacity = 1)]
#[init(B)]
b: B,
}

#[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
let mut poster = cx.poster;
assert!(poster.post(M::default()).is_ok());

// B's message queue is full so message must NOT be cloned
// this must also NOT trigger task A even if it has capacity
assert!(poster
.post(M {
must_not_be_cloned: true
})
.is_err());

(Shared {}, Local {}, init::Monotonics(), Actors {})
}

#[idle]
fn idle(_: idle::Context) -> ! {
loop {
debug::exit(debug::EXIT_SUCCESS)
}
}

#[local]
struct Local {}

#[shared]
struct Shared {}
}
Loading

0 comments on commit 00f09c8

Please sign in to comment.