1
1
use crate :: syntax:: { ast:: App , Context } ;
2
2
use crate :: { analyze:: Analysis , codegen:: bindings:: interrupt_mod, codegen:: util} ;
3
+
3
4
use proc_macro2:: TokenStream as TokenStream2 ;
4
5
use quote:: quote;
5
6
@@ -112,37 +113,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
112
113
let internal_context_name = util:: internal_task_ident ( name, "Context" ) ;
113
114
let exec_name = util:: internal_task_ident ( name, "EXEC" ) ;
114
115
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 {
146
117
let spawnee = & app. software_tasks [ name] ;
147
118
let priority = spawnee. args . priority ;
148
119
let cfgs = & spawnee. cfgs ;
@@ -163,13 +134,21 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
163
134
let ( input_args, input_tupled, input_untupled, input_ty) =
164
135
util:: regroup_inputs ( & spawnee. inputs ) ;
165
136
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
+
166
145
// Spawn caller
167
146
items. push ( quote ! (
168
147
#( #cfgs) *
169
148
/// Spawns the task directly
170
149
#[ allow( non_snake_case) ]
171
150
#[ 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> {
173
152
// SAFETY: If `try_allocate` succeeds one must call `spawn`, which we do.
174
153
unsafe {
175
154
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 {
204
183
}
205
184
) ) ;
206
185
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
+ }
212
250
213
251
module_items. push ( quote ! (
214
252
#( #cfgs) *
@@ -217,6 +255,36 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
217
255
) ) ;
218
256
}
219
257
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
+
220
288
if items. is_empty ( ) {
221
289
quote ! ( )
222
290
} else {
0 commit comments