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 defaultIdle
thread 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.