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
//! Rust Runtime for the Flipper Zero.
//!
//! This must be build with `-Z no-unique-section-names` to ensure that this module
//! is linked directly into the `.text` section.

#![no_std]

pub mod manifest;
pub mod panic_handler;

/// The C entry point.
/// This just delegates to the user's Rust entry point.
#[no_mangle]
pub unsafe extern "C" fn _start(args: *mut u8) -> i32 {
    extern "Rust" {
        fn main(args: *mut u8) -> i32;
    }

    main(args)
}

/// Define the entry point.
/// Must have the following signature: `fn(*mut u8) -> i32`.
#[macro_export]
macro_rules! entry {
    ($path:path) => {
        // Force the section to `.text` instead of `.text.main`.
        // lld seems not to automatically rename `.rel.text.main` properly.
        #[export_name = "main"]
        pub unsafe fn __main(args: *mut u8) -> i32 {
            use core::{ffi::CStr, mem, ptr, slice};
            use flipperzero_sys as sys;

            // type check the entry function
            let f: fn(*mut u8) -> i32 = $path;

            let ret = f(args);

            // Wait for threads with the same app ID to finish. We assume there are not
            // more than 30 threads running in the background outside this app, so we will
            // observe at least one of any thread the app might have left running.
            unsafe {
                sys::furi_log_print_format(
                    sys::FuriLogLevel_FuriLogLevelDebug,
                    sys::c_string!("flipperzero-rt"),
                    sys::c_string!("Waiting for FAP threads to complete..."),
                );
            }

            const MAX_THREADS: usize = 32;
            let cur_thread_id = unsafe { sys::furi_thread_get_current_id() };
            let app_id = unsafe { CStr::from_ptr(sys::furi_thread_get_appid(cur_thread_id)) };
            let mut thread_ids: [sys::FuriThreadId; MAX_THREADS] = [ptr::null_mut(); MAX_THREADS];

            'outer: loop {
                let thread_count = unsafe {
                    sys::furi_thread_enumerate(thread_ids.as_mut_ptr(), MAX_THREADS as u32)
                } as usize;

                for &thread_id in thread_ids[..thread_count].into_iter() {
                    let thread_app_id =
                        unsafe { CStr::from_ptr(sys::furi_thread_get_appid(thread_id)) };

                    if thread_id == cur_thread_id || thread_app_id != app_id {
                        // Ignore this thread or the threads of other apps
                        continue;
                    }

                    let thread_name =
                        unsafe { CStr::from_ptr(sys::furi_thread_get_name(thread_id)) };

                    if thread_name.to_bytes().ends_with(b"Srv") {
                        // This is a workaround for an issue where the current appid matches
                        // one of the built-in service names (e.g. "gui"). Otherwise we will
                        // see the service thread (e.g. "GuiSrv") and assume that it's one of
                        // our threads that still needs to exit, thus causing the app to hang
                        // at exit.
                        continue;
                    }

                    // There is a thread that is still running, so wait for it to exit...

                    unsafe {
                        sys::furi_delay_ms(10);
                    }

                    continue 'outer;
                }

                break;
            }

            unsafe {
                sys::furi_log_print_format(
                    sys::FuriLogLevel_FuriLogLevelDebug,
                    sys::c_string!("flipperzero-rt"),
                    sys::c_string!("All threads completed, exiting FAP"),
                );
            }

            ret
        }
    };
}