Module avr_oxide::concurrency::thread
source · Expand description
Multithreading primitives.
AVRoxide allows you to create multiple threads, which will be scheduled
cooperatively (through the yield_now() method, or by calling any
blocking I/O routine), or pre-emptively if a suitable interrupt source is
nominated for pre-emption via a crate feature flag.
§Limitations
There is a limit to the number of threads you can create, and additionally since each thread gets a stack allocated from the heap, the available heap memory is a limit.
The initial (main()) thread is allocated a relatively large default stack
size, in recognition that it may be the only thread in the program and that
it is also likely to allocate a lot of ‘global’ data on its own stack.
This stack size can be changed by passing the stacksize attribute to
the avr_oxide::main macro.
Subsequent threads are given a smaller default stack size, although this
can be overridden using the Builder::stack_size() method to create
the thread.
The following table summarises the default thread limits:
| For processor | Max threads | default main() stack | default new thread stack |
|---|---|---|---|
atmega4809 | 3 | 512 bytes | 128 bytes |
atmega328p | 3 | 384 bytes | 64 bytes |
Note: The maximum number of threads in the table includes the
main()thread, but not the defaultIdlethread that is created by the kernel and must always exist.
§Thread completion and cleanup
When a thread completes, it will enter a Zombie state. It will remain
in this state - and the thread context and stack will not be cleaned up,
releasing any associated memory - until another thread joins it using
the JoinHandle::join() method.
§Pre-Emptive Multithreading
Thread pre-emption depends on one (or more) interrupt sources being
nominated to drive the scheduler. This is done by enabling a
pmt_<interrupt_name> feature when including the AVRoxide crate in your
cargo.toml.
Typically, you would nominate a timer interrupt; which interrupt will depend on the device:
| For processor | Typical Feature flag | Effect |
|---|---|---|
atmega4809 | pmt_tcb0_int | Threads will be rescheduled every time TimerControlBlock 0 generates an interrupt |
§Example
#![no_std]
#![no_main]
use avr_oxide::devices::{ UsesPin, OxideLed, OxideMasterClock };
use avr_oxide::thread;
use avr_oxide::hardware;
use avr_oxide::boards::board;
use avr_oxide::StaticWrap;
#[avr_oxide::main(chip="atmega4809",stacksize=1024)]
pub fn main() {
let supervisor = avr_oxide::oxide::instance();
// Configure a 50Hz master clock device on TCB0
let master_clock = StaticWrap::new(OxideMasterClock::with_timer::<50>(hardware::timer::tcb0::instance()));
supervisor.listen(master_clock.borrow());
// If our code builds AVRoxide using the `pmt_tcb0_int` feature, threads
// will now be pre-emptively multitasked, and this code will work without
// locking up:
let _jh = thread::Builder::new().stack_size(32).spawn(||{
let white_led = OxideLed::with_pin(board::pin_d(10));
loop {
white_led.toggle();
}
});
// Note that all the usual functionality of the MasterClock device
// (delay timers, regular Oxide event handlers) remains available -
// it will be used for thread preemption *in addition* to its usual
// function, not instead.
supervisor.run();
}Structs§
- A thread factory that allows us to set the stack size for the thread that we create.
- Handle that allows us to join a thread
- The ‘userland facing’ representation of a Thread
Functions§
- Spawn a new thread, returning a JoinHandle for it. Panics if the thread cannot be spawned.
- Cooperatively have the current thread yield to another.