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
//! A simple RTOS based around Rust `Future`s. //! //! This provides a minimal operating environment for running async Rust code on //! ARM Cortex-M microprocessors, plus some useful doodads and gizmos. //! //! # About the OS //! //! This OS is designed around the notion of a fixed set of concurrent tasks //! that run forever. To use the OS, your application startup routine calls //! [`exec::run_tasks`], giving it an array of tasks you've defined; `run_tasks` //! never returns. //! //! The OS provides *cooperative* multitasking: while tasks are concurrent, they //! are not preemptive, and are not "threads" in the traditional sense. Tasks //! don't even have their own stacks -- they return completely whenever they //! yield the CPU. //! //! This would be incredibly frustrating to program, were it not for `Future` //! and `async`. //! //! Each task co-routine has the type `Future<Output = !>`, meaning a `Future` //! that can be polled but will never complete (because, remember, tasks run //! forever). The OS provides an *executor* that manages polling of a set of //! `Future`s. //! //! Rust's `async` keyword provides a convenient way to have the compiler //! rewrite a normal function into a co-routine-style `Future`. This means that //! writing co-routines to run on this OS looks *very much* like programming //! with threads. //! //! Here is the "hello world" of embedded programming, written as a task for //! this OS. This task blinks an LED attached to port D12 of an STM32F4. //! //! ```ignore //! async fn blinky(gpio: &GPIOD) -> ! { //! const PERIOD: Duration = Duration::from_millis(500); //! //! loop { //! gpio.bsrr.write(|w| w.bs12().set_bit()); //! os::exec::sleep_for(PERIOD).await; //! gpio.bsrr.write(|w| w.br12().set_bit()); //! os::exec::sleep_for(PERIOD).await; //! } //! } //! ``` //! //! Because `Future`s can be _composed_, the fixed set of OS tasks can drive a //! _dynamic_ set of program `Future`s. //! //! # Concurrency and interrupts //! //! The OS supports the use of interrupt handlers to wake tasks through the //! [`Notify`][exec::Notify] mechanism, but most OS facilities are not available //! in interrupt context. //! //! By default, interrupts are masked when task code is running, so tasks can be //! confident that they will preempted if, and only if, they `await`. //! //! Each time through the task polling loop, the OS unmasks interrupts to let //! any pending interrupts run. Because the Cortex-M collects pending interrupts //! while interrupts are masked, we don't run the risk of missing events. //! //! Interrupts are also unmasked whenever the idle processor is woken from //! sleep, in order to handle the event that woke it up. //! //! See the [`exec`][crate::exec] module for more details and some customization //! options. //! //! # Cancellation //! //! Co-routine tasks in this OS are just `Future`s, which means they can be //! dropped. `Future`s are typically dropped just after they resolve (often just //! after an `await` keyword in the calling code), but it's also possible to //! drop a `Future` while it is pending. This can happen explicitly (by calling //! [`drop`]), or as a side effect of other operations; for example, the future //! returned by //! [`select!`](https://docs.rs/futures/0.3/futures/macro.select.html) will drop //! any uncompleted futures if you `await` it. //! //! This means it's useful to consider what cancellation *means* for any //! particular task, and to ensure that its results are what you intend. //! //! OS-provided futures attempt to provide useful cancellation behavior. #![no_std] // We need `never_type` to be able to write `Future<Output = !>`, the type of // `async fn foo() -> !`. This feature has been near stabilization for years, // but as of spring 2021 it is still unstable. #![feature(never_type)] #[macro_use] pub mod list; pub mod exec; pub mod time; #[cfg(feature = "mutex")] pub mod mutex; #[cfg(feature = "spsc")] pub mod spsc; #[cfg(feature = "handoff")] pub mod handoff; use core::marker::PhantomData; /// Zero-sized marker type that can be included to ensure that a data structure /// is not automatically made `Sync` (i.e. safe for sharing across threads). /// /// A type that includes this may still be inferred as `Send`. #[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] struct NotSyncMarker(PhantomData<core::cell::Cell<()>>); /// Zero-sized marker type that can be included to ensure that a data structure /// is not automatically made `Send` (i.e. safe for transfer across threads). /// /// This also blocks `Sync`. #[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] struct NotSendMarker(PhantomData<*const ()>);