Skip to content

Commit 122813a

Browse files
committed
Merge branch 'main' into in_thread_token
2 parents 8931519 + d2629c9 commit 122813a

File tree

6 files changed

+273
-129
lines changed

6 files changed

+273
-129
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "riot-wrappers"
33
version = "0.8.0"
44
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
55
edition = "2021"
6+
rust-version = "1.65"
67

78
description = "Rust API wrappers for the RIOT operating system"
89
documentation = "https://rustdoc.etonomy.org/riot_wrappers/"

src/main_module.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,62 @@ use crate::stdio::println;
2828

2929
use core::fmt;
3030

31+
mod sealed {
32+
pub trait Sealed<Variant> {}
33+
}
34+
35+
use sealed::Sealed;
36+
37+
// The Variant argument is really just taking different types to allow "conflicting"
38+
// implementations that are not conflicting but just ambiguous as long as nobody forces the Variant
39+
// argument. Conveniently, that ambiguity is accepted.
40+
//
41+
// Thanks to Charles from #rust:matrix.org for pointing out this neat trick.
42+
#[doc(hidden)]
43+
pub trait UsableAsMain<Variant>: Sealed<Variant> {
44+
unsafe fn call_main(&self) -> i32;
45+
}
46+
47+
// Beware that the following are *not* checked for being conflicting (because they are not), but if
48+
// there were any situation of ambiguity, the main macro would break.
49+
50+
impl<F: Fn() -> T, T: Termination> Sealed<[u8; 1]> for F {}
51+
52+
impl<F: Fn() -> T, T: Termination> UsableAsMain<[u8; 1]> for F {
53+
unsafe fn call_main(&self) -> i32 {
54+
(self)().report()
55+
}
56+
}
57+
58+
impl<F: Fn(crate::thread::StartToken) -> crate::never::Never> Sealed<[u8; 2]> for F {}
59+
60+
impl<F: Fn(crate::thread::StartToken) -> crate::never::Never> UsableAsMain<[u8; 2]> for F {
61+
unsafe fn call_main(&self) -> i32 {
62+
// unsafe: By construction of the C main function this only happens at startup time
63+
// with a thread that hasn't done anything relevant before.
64+
let unique = crate::thread::StartToken::new();
65+
66+
(self)(unique)
67+
}
68+
}
69+
70+
impl<F: Fn(crate::thread::StartToken) -> ((), crate::thread::EndToken)> Sealed<[u8; 3]> for F {}
71+
72+
impl<F: Fn(crate::thread::StartToken) -> ((), crate::thread::EndToken)> UsableAsMain<[u8; 3]>
73+
for F
74+
{
75+
unsafe fn call_main(&self) -> i32 {
76+
// unsafe: By construction of the C main function this only happens at startup time
77+
// with a thread that hasn't done anything relevant before.
78+
let unique = crate::thread::StartToken::new();
79+
80+
// We're not really consuming the token, just require that the function can provide it and
81+
// doesn't just return without having invalidated all users of its PID
82+
let (termination, _token) = (self)(unique);
83+
termination.report()
84+
}
85+
}
86+
3187
/// To have a nice Rust main function, run the `riot_main!` macro with the name of your main
3288
/// function an item (ie. top level in a module) in your crate. The function identified by it must
3389
/// return something that implements the Termination trait.
@@ -43,29 +99,36 @@ use core::fmt;
4399
/// unimplemented!()
44100
/// }
45101
/// ```
102+
///
103+
/// Functions with multiple signatures are accepted:
104+
///
105+
/// * `fn main()` -- useful for very simple programs
106+
/// * `fn main() -> impl Termination` -- prints the error message according to the [Termination]
107+
/// implementation (in particular, [Result] types with a [Debug] error are useful here)
108+
/// * `fn main(tokens: StartToken) -> (impl Termination, EndToken)` -- this ensures that
109+
/// the program has full control over the main thread. As a [StartToken] allows doing things that
110+
/// require undoing before the thread may terminate (eg. subscribing it to messages), an
111+
/// [EndToken] needs to be produced before the thread can terminate with a message as
112+
/// above.
113+
/// * `fn main(tokens: StartToken) -> !` -- a frequently useful variation thereof for main loops
114+
/// that are loops anyway.
46115
#[macro_export]
47116
macro_rules! riot_main {
48117
($main:ident) => {
49118
#[export_name = "main"]
50119
pub extern "C" fn c_main() -> i32 {
51-
use riot_wrappers::main::Termination;
52-
$main().report()
120+
unsafe { <_ as $crate::main::UsableAsMain<_>>::call_main(&$main) }
53121
}
54122
};
55123
}
56124

125+
#[deprecated(note = "Use `riot_main` instead, which takes multiple signatures")]
57126
#[macro_export]
58127
macro_rules! riot_main_with_tokens {
59128
($main:ident) => {
60129
#[export_name = "main"]
61130
pub extern "C" fn c_main() -> i32 {
62-
// unsafe: By construction of the C main function this only happens at startup time
63-
// with a thread that hasn't done anything relevant before.
64-
let unique = unsafe { riot_wrappers::thread::StartToken::new() };
65-
66-
let (result, token): (_, riot_wrappers::thread::TerminationToken) = $main(unique);
67-
use riot_wrappers::main::Termination;
68-
result.report()
131+
unsafe { <_ as $crate::main::UsableAsMain<_>>::call_main(&$main) }
69132
}
70133
};
71134
}

0 commit comments

Comments
 (0)