Skip to content

Commit 121fe3e

Browse files
actor API syntax
see rtic-rs/rfcs#52 for details includes: core proposal and first `#[init]` extension Co-authored-by: Jonas Schievink <[email protected]>
1 parent 14c211d commit 121fe3e

File tree

8 files changed

+454
-19
lines changed

8 files changed

+454
-19
lines changed

src/analyze.rs

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ use crate::{
1111
Set,
1212
};
1313

14+
type TaskName = String;
15+
1416
pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
1517
// Collect all tasks into a vector
16-
type TaskName = String;
17-
type Priority = u8;
1818

1919
// The task list is a Tuple (Name, Shared Resources, Local Resources, Priority)
2020
let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> =
@@ -252,23 +252,51 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
252252
let spawnee_prio = spawnee.args.priority;
253253

254254
let channel = channels.entry(spawnee_prio).or_default();
255-
channel.tasks.insert(name.clone());
255+
channel
256+
.spawnees
257+
.insert(Spawnee::Task { name: name.clone() });
256258

257259
// All inputs are now send as we do not know from where they may be spawned.
258260
spawnee.inputs.iter().for_each(|input| {
259261
send_types.insert(input.ty.clone());
260262
});
261263
}
262264

265+
for (name, actor) in &app.actors {
266+
let spawnee_prio = actor.priority;
267+
268+
if !actor.subscriptions.is_empty() {
269+
for (index, subscription) in actor.subscriptions.iter().enumerate() {
270+
let channel = channels.entry(spawnee_prio).or_default();
271+
channel.spawnees.insert(Spawnee::Actor {
272+
name: name.clone(),
273+
subscription_index: index,
274+
});
275+
276+
// All inputs are now send as we do not know from where they may be spawned.
277+
send_types.insert(Box::new(subscription.ty.clone()));
278+
}
279+
}
280+
}
281+
263282
// No channel should ever be empty
264-
debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty()));
283+
debug_assert!(channels
284+
.values()
285+
.all(|channel| !channel.spawnees.is_empty()));
265286

266287
// Compute channel capacities
267288
for channel in channels.values_mut() {
268289
channel.capacity = channel
269-
.tasks
290+
.spawnees
270291
.iter()
271-
.map(|name| app.software_tasks[name].args.capacity)
292+
.map(|spawnee| match spawnee {
293+
Spawnee::Task { name } => app.software_tasks[name].args.capacity,
294+
Spawnee::Actor {
295+
name,
296+
subscription_index,
297+
..
298+
} => app.actors[name].subscriptions[*subscription_index].capacity,
299+
})
272300
.sum();
273301
}
274302

@@ -345,8 +373,25 @@ pub struct Channel {
345373
/// The channel capacity
346374
pub capacity: u8,
347375

348-
/// Tasks that can be spawned on this channel
349-
pub tasks: BTreeSet<Task>,
376+
/// Tasks / AO that can be spawned on this channel
377+
pub spawnees: BTreeSet<Spawnee>,
378+
}
379+
380+
/// What can be spawned
381+
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
382+
pub enum Spawnee {
383+
/// A software task
384+
Task {
385+
/// The name of the task
386+
name: Task,
387+
},
388+
/// An actor
389+
Actor {
390+
/// The name of the actor
391+
name: Ident,
392+
/// Index into the actor's `subscriptions` field
393+
subscription_index: usize,
394+
},
350395
}
351396

352397
/// Resource ownership

src/ast.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub struct App {
2929
/// Task local resources defined in `#[local]`
3030
pub local_resources: Map<LocalResource>,
3131

32+
/// Actors defined in the `#[actors]` struct
33+
pub actors: Map<Actor>,
34+
3235
/// User imports
3336
pub user_imports: Vec<ItemUse>,
3437

@@ -90,6 +93,9 @@ pub struct Init {
9093

9194
/// The name of the user provided local resources struct
9295
pub user_local_struct: Ident,
96+
97+
/// The name of the user provided actors struct
98+
pub user_actors_struct: Option<Ident>,
9399
}
94100

95101
/// `init` context metadata
@@ -192,6 +198,31 @@ pub struct LocalResource {
192198
pub ty: Box<Type>,
193199
}
194200

201+
/// An actor defined in the `#[actors]` struct.
202+
#[derive(Debug)]
203+
#[non_exhaustive]
204+
pub struct Actor {
205+
/// The priority of this actor
206+
pub priority: u8,
207+
/// The expression used to initialized this actor. If absent, uses late/runtime
208+
/// initialization.
209+
pub init: Option<Box<Expr>>,
210+
/// Type of the actor.
211+
pub ty: Box<Type>,
212+
/// #[subscribe] attributes
213+
pub subscriptions: Vec<Subscription>,
214+
}
215+
216+
/// The `#[subscribe]` attribute of an actor
217+
#[derive(Debug)]
218+
#[non_exhaustive]
219+
pub struct Subscription {
220+
/// Capacity of this channel
221+
pub capacity: u8,
222+
/// Message type
223+
pub ty: Type,
224+
}
225+
195226
/// Monotonic
196227
#[derive(Debug)]
197228
#[non_exhaustive]

src/parse.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod actor;
12
mod app;
23
mod hardware_task;
34
mod idle;

src/parse/actor.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use proc_macro2::{Ident, Span};
2+
use syn::{
3+
parenthesized,
4+
parse::{self, Parse},
5+
spanned::Spanned,
6+
Field, LitInt, Token, Type, Visibility,
7+
};
8+
9+
use crate::ast::{Actor, Subscription};
10+
11+
use super::util::{self, FilterAttrs};
12+
13+
impl Actor {
14+
pub(crate) fn parse(item: &Field, span: Span) -> parse::Result<Self> {
15+
if item.vis != Visibility::Inherited {
16+
return Err(parse::Error::new(
17+
span,
18+
"this field must have inherited / private visibility",
19+
));
20+
}
21+
22+
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs.clone());
23+
24+
if !cfgs.is_empty() {
25+
return Err(parse::Error::new(span, "`#[cfg]` is not allowed on actors"));
26+
}
27+
28+
let mut priority = None;
29+
let mut init = None;
30+
let mut subscriptions = Vec::new();
31+
32+
for attr in attrs {
33+
match attr.path.get_ident() {
34+
Some(name) => {
35+
match &*name.to_string() {
36+
"priority" => {
37+
if priority.is_some() {
38+
return Err(parse::Error::new(
39+
attr.span(),
40+
"only one `#[priority]` attribute is allowed on an actor",
41+
));
42+
}
43+
44+
let prio: EqPriority = syn::parse2(attr.tokens)?;
45+
priority = Some(prio.priority);
46+
}
47+
"init" => {
48+
if init.is_some() {
49+
return Err(parse::Error::new(
50+
attr.span(),
51+
"only one `#[init]` attribute is allowed on an actor",
52+
));
53+
}
54+
55+
// `#[init(expr)]` can be parsed via `ExprParen`
56+
let paren: syn::ExprParen = syn::parse2(attr.tokens)?;
57+
58+
init = Some(paren.expr);
59+
}
60+
"subscribe" => {
61+
let subscribe: Subscribe = syn::parse2(attr.tokens)?;
62+
let capacity = subscribe
63+
.capacity
64+
.map(|lit| {
65+
lit.base10_digits().parse::<u8>().map_err(|_| {
66+
parse::Error::new(lit.span(), "not a `u8` value")
67+
})
68+
})
69+
.transpose()?;
70+
71+
subscriptions.push(Subscription {
72+
ty: subscribe.ty,
73+
capacity: capacity.unwrap_or(1),
74+
});
75+
}
76+
_ => {
77+
return Err(parse::Error::new(
78+
name.span(),
79+
"this attribute is not supported on actor declarations",
80+
));
81+
}
82+
}
83+
}
84+
None => {
85+
return Err(parse::Error::new(
86+
attr.path.span(),
87+
"this attribute is not supported on actor declarations",
88+
));
89+
}
90+
}
91+
}
92+
93+
Ok(Actor {
94+
ty: Box::new(item.ty.clone()),
95+
priority: priority.unwrap_or(1),
96+
init,
97+
subscriptions,
98+
})
99+
}
100+
}
101+
102+
struct EqPriority {
103+
priority: u8,
104+
}
105+
106+
impl parse::Parse for EqPriority {
107+
fn parse(input: parse::ParseStream<'_>) -> syn::Result<Self> {
108+
let _eq: Token![=] = input.parse()?;
109+
let lit: syn::LitInt = input.parse()?;
110+
111+
if !lit.suffix().is_empty() {
112+
return Err(parse::Error::new(
113+
lit.span(),
114+
"this literal must be unsuffixed",
115+
));
116+
}
117+
118+
let value = lit.base10_parse::<u8>().ok();
119+
match value {
120+
None | Some(0) => Err(parse::Error::new(
121+
lit.span(),
122+
"this literal must be in the range 1...255",
123+
)),
124+
Some(priority) => Ok(Self { priority }),
125+
}
126+
}
127+
}
128+
129+
struct Subscribe {
130+
ty: Type,
131+
capacity: Option<LitInt>,
132+
}
133+
134+
impl Parse for Subscribe {
135+
fn parse(input: parse::ParseStream<'_>) -> syn::Result<Self> {
136+
let content;
137+
parenthesized!(content in input);
138+
139+
let ty = content.parse()?;
140+
141+
let capacity = if content.is_empty() {
142+
None
143+
} else {
144+
let _: Token![,] = content.parse()?;
145+
let ident: Ident = content.parse()?;
146+
147+
if ident.to_string() == "capacity" {
148+
let _: Token![=] = content.parse()?;
149+
Some(content.parse()?)
150+
} else {
151+
return Err(parse::Error::new(
152+
ident.span(),
153+
format!("expected `capacity`, found `{}`", ident),
154+
));
155+
}
156+
};
157+
158+
Ok(Self { ty, capacity })
159+
}
160+
}

0 commit comments

Comments
 (0)