aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen/async_dispatchers.rs
blob: be02ad09a1d7111ec1ffcca030a4047c01d24f50 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::syntax::ast::App;
use crate::{analyze::Analysis, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

/// Generates task dispatchers
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
    let mut items = vec![];

    let interrupts = &analysis.interrupts;

    // Generate executor definition and priority in global scope
    for (name, _) in app.software_tasks.iter() {
        let type_name = util::internal_task_ident(name, "F");
        let exec_name = util::internal_task_ident(name, "EXEC");

        items.push(quote!(
            #[allow(non_camel_case_types)]
            type #type_name = impl core::future::Future + 'static;
            #[allow(non_upper_case_globals)]
            static #exec_name:
                rtic::RacyCell<rtic::export::executor::AsyncTaskExecutor<#type_name>> =
                    rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new());
        ));
    }

    for (&level, channel) in &analysis.channels {
        let mut stmts = vec![];

        let dispatcher_name = if level > 0 {
            util::suffixed(&interrupts.get(&level).expect("UNREACHABLE").0.to_string())
        } else {
            util::zero_prio_dispatcher_ident()
        };

        let pend_interrupt = if level > 0 {
            let device = &app.args.device;
            let enum_ = util::interrupt_ident();

            quote!(rtic::pend(#device::#enum_::#dispatcher_name);)
        } else {
            // For 0 priority tasks we don't need to pend anything
            quote!()
        };

        for name in channel.tasks.iter() {
            let exec_name = util::internal_task_ident(name, "EXEC");
            // let task = &app.software_tasks[name];
            // let cfgs = &task.cfgs;
            let executor_run_ident = util::executor_run_ident(name);

            let rq = util::rq_async_ident(name);

            items.push(quote!(
                #[doc(hidden)]
                #[allow(non_camel_case_types)]
                #[allow(non_upper_case_globals)]
                static #rq: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
            ));

            stmts.push(quote!(
                if !(&*#exec_name.get()).is_running() {
                    // TODO Fix this to be compare and swap
                    if #rq.load(core::sync::atomic::Ordering::Relaxed) {
                        #rq.store(false, core::sync::atomic::Ordering::Relaxed);

                        (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new()));
                        #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed);
                    }
                }

                if #executor_run_ident.load(core::sync::atomic::Ordering::Relaxed) {
                    #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed);
                    if (&mut *#exec_name.get_mut()).poll(||  {
                        #executor_run_ident.store(true, core::sync::atomic::Ordering::Release);
                        #pend_interrupt
                    }) && #rq.load(core::sync::atomic::Ordering::Relaxed) {
                        // If the ready queue is not empty and the executor finished, restart this
                        // dispatch to check if the executor should be restarted.
                        #pend_interrupt
                    }
                }
            ));
        }

        if level > 0 {
            let doc = format!(
                "Interrupt handler to dispatch async tasks at priority {level}"
            );
            let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs;
            items.push(quote!(
                #[allow(non_snake_case)]
                #[doc = #doc]
                #[no_mangle]
                #(#attribute)*
                unsafe fn #dispatcher_name() {
                    /// The priority of this interrupt handler
                    const PRIORITY: u8 = #level;

                    rtic::export::run(PRIORITY, || {
                        // Have the acquire/release semantics outside the checks to no overdo it
                        core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire);

                        #(#stmts)*

                        core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
                    });
                }
            ));
        } else {
            items.push(quote!(
                #[allow(non_snake_case)]
                unsafe fn #dispatcher_name() -> ! {
                    loop {
                        // Have the acquire/release semantics outside the checks to no overdo it
                        core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire);

                        #(#stmts)*

                        core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
                    }
                }
            ));
        }
    }

    quote!(#(#items)*)
}