Skip to content

Commit f2e467f

Browse files
actor API
see rtic-rs/rfcs#52 for details includes: core proposal and `#[init]` and `memory-watermark` extensions Co-authored-by: Jonas Schievink <[email protected]>
1 parent 8d3c803 commit f2e467f

27 files changed

+1095
-69
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ name = "rtic"
2222
[dependencies]
2323
cortex-m = "0.7.0"
2424
cortex-m-rtic-macros = { path = "macros", version = "1.1.1" }
25+
rtic-actor-traits = { path = "actor-traits" }
2526
rtic-monotonic = "1.0.0"
2627
rtic-core = "1.0.0"
2728
heapless = "0.7.7"
@@ -42,13 +43,18 @@ version = "0.5.2"
4243
[target.x86_64-unknown-linux-gnu.dev-dependencies]
4344
trybuild = "1"
4445

46+
[features]
47+
memory-watermark = ["cortex-m-rtic-macros/memory-watermark"]
48+
4549
[profile.release]
4650
codegen-units = 1
4751
lto = true
4852

4953
[workspace]
5054
members = [
55+
"actor-traits",
5156
"macros",
57+
"post-spy",
5258
"xtask",
5359
]
5460

actor-traits/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
edition = "2018"
3+
name = "rtic-actor-traits"
4+
version = "0.1.0"
5+
6+
[dependencies]

actor-traits/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![no_std]
2+
3+
pub trait Post<M> {
4+
fn post(&mut self, message: M) -> Result<(), M>;
5+
}
6+
7+
pub trait Receive<M> {
8+
fn receive(&mut self, message: M);
9+
}

examples/actor-capacity.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use panic_semihosting as _;
5+
6+
#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])]
7+
mod app {
8+
use core::sync::atomic::{AtomicU8, Ordering};
9+
10+
use cortex_m_semihosting::{debug, hprintln};
11+
use rtic_actor_traits::Receive;
12+
13+
struct Actor;
14+
15+
struct Message;
16+
17+
static CALL_COUNT: AtomicU8 = AtomicU8::new(0);
18+
19+
impl Receive<Message> for Actor {
20+
fn receive(&mut self, _: Message) {
21+
hprintln!("Actor::receive was called").ok();
22+
CALL_COUNT.store(CALL_COUNT.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
23+
}
24+
}
25+
26+
#[actors]
27+
struct Actors {
28+
#[subscribe(Message, capacity = 2)]
29+
actor: Actor,
30+
}
31+
32+
#[init]
33+
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
34+
assert!(cx.poster.post(Message).is_ok());
35+
assert!(cx.poster.post(Message).is_ok());
36+
assert!(cx.poster.post(Message).is_err());
37+
38+
(
39+
Shared {},
40+
Local {},
41+
init::Monotonics(),
42+
Actors { actor: Actor },
43+
)
44+
}
45+
46+
#[idle]
47+
fn idle(_: idle::Context) -> ! {
48+
assert_eq!(2, CALL_COUNT.load(Ordering::Relaxed));
49+
50+
loop {
51+
debug::exit(debug::EXIT_SUCCESS);
52+
}
53+
}
54+
55+
#[shared]
56+
struct Shared {}
57+
58+
#[local]
59+
struct Local {}
60+
}

examples/actor-init.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use panic_semihosting as _;
5+
6+
#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])]
7+
mod app {
8+
use cortex_m_semihosting::{debug, hprintln};
9+
use rtic_actor_traits::Receive;
10+
11+
#[derive(Debug)]
12+
struct Actor {
13+
state: i32,
14+
}
15+
16+
struct AssertActorWasInitialized;
17+
18+
const INITIAL_STATE: i32 = 42;
19+
20+
impl Receive<AssertActorWasInitialized> for Actor {
21+
fn receive(&mut self, _: AssertActorWasInitialized) {
22+
assert_eq!(INITIAL_STATE, self.state);
23+
hprintln!("OK").ok();
24+
debug::exit(debug::EXIT_SUCCESS);
25+
}
26+
}
27+
28+
#[actors]
29+
struct Actors {
30+
#[subscribe(AssertActorWasInitialized)]
31+
#[init(Actor { state: INITIAL_STATE })]
32+
actor: Actor,
33+
}
34+
35+
#[init]
36+
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
37+
cx.poster.post(AssertActorWasInitialized).ok();
38+
39+
(Shared {}, Local {}, init::Monotonics(), Actors {})
40+
}
41+
42+
#[shared]
43+
struct Shared {}
44+
45+
#[local]
46+
struct Local {}
47+
}

examples/actor-post.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use panic_semihosting as _;
5+
6+
#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])]
7+
mod app {
8+
use core::sync::atomic::{AtomicBool, Ordering};
9+
10+
use cortex_m_semihosting::{debug, hprintln};
11+
use rtic_actor_traits::Receive;
12+
13+
struct Actor;
14+
15+
const PAYLOAD: i32 = 42;
16+
17+
struct Message {
18+
payload: i32,
19+
}
20+
21+
static RECEIVE_WAS_CALLED: AtomicBool = AtomicBool::new(false);
22+
23+
impl Receive<Message> for Actor {
24+
fn receive(&mut self, m: Message) {
25+
hprintln!("Actor::receive was called").ok();
26+
27+
RECEIVE_WAS_CALLED.store(true, Ordering::Relaxed);
28+
29+
assert_eq!(PAYLOAD, m.payload);
30+
}
31+
}
32+
33+
#[actors]
34+
struct Actors {
35+
#[subscribe(Message)]
36+
actor: Actor,
37+
}
38+
39+
#[init]
40+
fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
41+
cx.poster.post(Message { payload: PAYLOAD }).ok();
42+
43+
// receive invocation withheld
44+
assert!(!RECEIVE_WAS_CALLED.load(Ordering::Relaxed));
45+
46+
(
47+
Shared {},
48+
Local {},
49+
init::Monotonics(),
50+
Actors { actor: Actor },
51+
)
52+
}
53+
54+
#[idle]
55+
fn idle(_: idle::Context) -> ! {
56+
// receive invocation must have executed by now
57+
assert!(RECEIVE_WAS_CALLED.load(Ordering::Relaxed));
58+
59+
loop {
60+
debug::exit(debug::EXIT_SUCCESS);
61+
}
62+
}
63+
64+
#[shared]
65+
struct Shared {}
66+
67+
#[local]
68+
struct Local {}
69+
}

examples/actor-publish-failure.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use panic_semihosting as _;
5+
6+
#[rtic::app(device = lm3s6965, dispatchers = [GPIOA, GPIOB])]
7+
mod app {
8+
use core::sync::atomic::{AtomicBool, Ordering};
9+
10+
use cortex_m_semihosting::{debug, hprintln};
11+
use rtic_actor_traits::Receive;
12+
13+
struct A;
14+
struct B;
15+
16+
#[derive(Default)]
17+
struct M {
18+
must_not_be_cloned: bool,
19+
}
20+
21+
impl Clone for M {
22+
fn clone(&self) -> Self {
23+
assert!(!self.must_not_be_cloned);
24+
M {
25+
must_not_be_cloned: self.must_not_be_cloned,
26+
}
27+
}
28+
}
29+
30+
impl Receive<M> for A {
31+
fn receive(&mut self, _: M) {
32+
static WAS_CALLED_EXACTLY_ONCE: AtomicBool = AtomicBool::new(false);
33+
hprintln!("A::receive was called").ok();
34+
assert!(!WAS_CALLED_EXACTLY_ONCE.load(Ordering::Relaxed));
35+
WAS_CALLED_EXACTLY_ONCE.store(true, Ordering::Relaxed);
36+
}
37+
}
38+
39+
impl Receive<M> for B {
40+
fn receive(&mut self, _: M) {
41+
static WAS_CALLED_EXACTLY_ONCE: AtomicBool = AtomicBool::new(false);
42+
hprintln!("B::receive was called").ok();
43+
assert!(!WAS_CALLED_EXACTLY_ONCE.load(Ordering::Relaxed));
44+
WAS_CALLED_EXACTLY_ONCE.store(true, Ordering::Relaxed);
45+
}
46+
}
47+
48+
#[actors]
49+
struct Actors {
50+
#[subscribe(M, capacity = 2)]
51+
#[init(A)]
52+
a: A,
53+
54+
#[subscribe(M, capacity = 1)]
55+
#[init(B)]
56+
b: B,
57+
}
58+
59+
#[init]
60+
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
61+
let mut poster = cx.poster;
62+
assert!(poster.post(M::default()).is_ok());
63+
64+
// B's message queue is full so message must NOT be cloned
65+
// this must also NOT trigger task A even if it has capacity
66+
assert!(poster
67+
.post(M {
68+
must_not_be_cloned: true
69+
})
70+
.is_err());
71+
72+
(Shared {}, Local {}, init::Monotonics(), Actors {})
73+
}
74+
75+
#[idle]
76+
fn idle(_: idle::Context) -> ! {
77+
loop {
78+
debug::exit(debug::EXIT_SUCCESS)
79+
}
80+
}
81+
82+
#[local]
83+
struct Local {}
84+
85+
#[shared]
86+
struct Shared {}
87+
}

0 commit comments

Comments
 (0)