11use crate :: syntax:: { ast:: App , Context } ;
22use crate :: { analyze:: Analysis , codegen:: bindings:: interrupt_mod, codegen:: util} ;
3+
34use proc_macro2:: TokenStream as TokenStream2 ;
45use quote:: quote;
56
@@ -112,37 +113,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
112113 let internal_context_name = util:: internal_task_ident ( name, "Context" ) ;
113114 let exec_name = util:: internal_task_ident ( name, "EXEC" ) ;
114115
115- items. push ( quote ! (
116- #( #cfgs) *
117- /// Execution context
118- #[ allow( non_snake_case) ]
119- #[ allow( non_camel_case_types) ]
120- pub struct #internal_context_name<' a> {
121- #[ doc( hidden) ]
122- __rtic_internal_p: :: core:: marker:: PhantomData <& ' a ( ) >,
123- #( #fields, ) *
124- }
125-
126- #( #cfgs) *
127- impl <' a> #internal_context_name<' a> {
128- #[ inline( always) ]
129- #[ allow( missing_docs) ]
130- pub unsafe fn new( #core) -> Self {
131- #internal_context_name {
132- __rtic_internal_p: :: core:: marker:: PhantomData ,
133- #( #values, ) *
134- }
135- }
136- }
137- ) ) ;
138-
139- module_items. push ( quote ! (
140- #( #cfgs) *
141- #[ doc( inline) ]
142- pub use super :: #internal_context_name as Context ;
143- ) ) ;
144-
145- if let Context :: SoftwareTask ( ..) = ctxt {
116+ if let Context :: SoftwareTask ( t) = ctxt {
146117 let spawnee = & app. software_tasks [ name] ;
147118 let priority = spawnee. args . priority ;
148119 let cfgs = & spawnee. cfgs ;
@@ -163,13 +134,21 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
163134 let ( input_args, input_tupled, input_untupled, input_ty) =
164135 util:: regroup_inputs ( & spawnee. inputs ) ;
165136
137+ let is_local_task = app. software_tasks [ t] . args . is_local_task ;
138+ let unsafety = if is_local_task {
139+ // local tasks are only safe to call from the same executor
140+ quote ! { unsafe }
141+ } else {
142+ quote ! { }
143+ } ;
144+
166145 // Spawn caller
167146 items. push ( quote ! (
168147 #( #cfgs) *
169148 /// Spawns the task directly
170149 #[ allow( non_snake_case) ]
171150 #[ doc( hidden) ]
172- pub fn #internal_spawn_ident( #( #input_args, ) * ) -> :: core:: result:: Result <( ) , #input_ty> {
151+ pub #unsafety fn #internal_spawn_ident( #( #input_args, ) * ) -> :: core:: result:: Result <( ) , #input_ty> {
173152 // SAFETY: If `try_allocate` succeeds one must call `spawn`, which we do.
174153 unsafe {
175154 let exec = rtic:: export:: executor:: AsyncTaskExecutor :: #from_ptr_n_args( #name, & #exec_name) ;
@@ -204,11 +183,70 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
204183 }
205184 ) ) ;
206185
207- module_items. push ( quote ! (
208- #( #cfgs) *
209- #[ doc( inline) ]
210- pub use super :: #internal_spawn_ident as spawn;
211- ) ) ;
186+ if !is_local_task {
187+ module_items. push ( quote ! (
188+ #( #cfgs) *
189+ #[ doc( inline) ]
190+ pub use super :: #internal_spawn_ident as spawn;
191+ ) ) ;
192+ }
193+
194+ let local_tasks_on_same_executor: Vec < _ > = app
195+ . software_tasks
196+ . iter ( )
197+ . filter ( |( _, t) | t. args . is_local_task && t. args . priority == priority)
198+ . collect ( ) ;
199+
200+ if !local_tasks_on_same_executor. is_empty ( ) {
201+ let local_spawner = util:: internal_task_ident ( t, "LocalSpawner" ) ;
202+ fields. push ( quote ! {
203+ /// Used to spawn tasks on the same executor
204+ ///
205+ /// This is useful for tasks that take args which are !Send/!Sync.
206+ ///
207+ /// NOTE: This only works with tasks marked `is_local_task = true`
208+ /// and which have the same priority and thus will run on the
209+ /// same executor.
210+ pub local_spawner: #local_spawner
211+ } ) ;
212+ let tasks = local_tasks_on_same_executor
213+ . iter ( )
214+ . map ( |( ident, task) | {
215+ // Copied mostly from software_tasks.rs
216+ let internal_spawn_ident = util:: internal_task_ident ( ident, "spawn" ) ;
217+ let attrs = & task. attrs ;
218+ let cfgs = & task. cfgs ;
219+ let inputs = & task. inputs ;
220+ let generics = if task. is_bottom {
221+ quote ! ( )
222+ } else {
223+ quote ! ( <' a>)
224+ } ;
225+ let input_vals = inputs. iter ( ) . map ( |i| & i. pat ) . collect :: < Vec < _ > > ( ) ;
226+ let ( _input_args, _input_tupled, _input_untupled, input_ty) = util:: regroup_inputs ( & task. inputs ) ;
227+ quote ! {
228+ #( #attrs) *
229+ #( #cfgs) *
230+ #[ allow( non_snake_case) ]
231+ pub ( super ) fn #ident #generics( & self #( , #inputs) * ) -> :: core:: result:: Result <( ) , #input_ty> {
232+ // SAFETY: This is safe to call since this can only be called
233+ // from the same executor
234+ unsafe { #internal_spawn_ident( #( #input_vals, ) * ) }
235+ }
236+ }
237+ } )
238+ . collect :: < Vec < _ > > ( ) ;
239+ values. push ( quote ! ( local_spawner: #local_spawner { _p: core:: marker:: PhantomData } ) ) ;
240+ items. push ( quote ! {
241+ struct #local_spawner {
242+ _p: core:: marker:: PhantomData <* mut ( ) >,
243+ }
244+
245+ impl #local_spawner {
246+ #( #tasks) *
247+ }
248+ } ) ;
249+ }
212250
213251 module_items. push ( quote ! (
214252 #( #cfgs) *
@@ -217,6 +255,36 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
217255 ) ) ;
218256 }
219257
258+ items. push ( quote ! (
259+ #( #cfgs) *
260+ /// Execution context
261+ #[ allow( non_snake_case) ]
262+ #[ allow( non_camel_case_types) ]
263+ pub struct #internal_context_name<' a> {
264+ #[ doc( hidden) ]
265+ __rtic_internal_p: :: core:: marker:: PhantomData <& ' a ( ) >,
266+ #( #fields, ) *
267+ }
268+
269+ #( #cfgs) *
270+ impl <' a> #internal_context_name<' a> {
271+ #[ inline( always) ]
272+ #[ allow( missing_docs) ]
273+ pub unsafe fn new( #core) -> Self {
274+ #internal_context_name {
275+ __rtic_internal_p: :: core:: marker:: PhantomData ,
276+ #( #values, ) *
277+ }
278+ }
279+ }
280+ ) ) ;
281+
282+ module_items. push ( quote ! (
283+ #( #cfgs) *
284+ #[ doc( inline) ]
285+ pub use super :: #internal_context_name as Context ;
286+ ) ) ;
287+
220288 if items. is_empty ( ) {
221289 quote ! ( )
222290 } else {
0 commit comments