Synchronization
Flint’s synchronization model: concurrency is a library feature, not a language syntax feature. There is no async/await, no channel operators, no goroutines. Blocking and non-blocking operations are ordinary function calls.
core/sync: Low-Level Primitives
Atomics
use core/sync
static COUNTER: sync.AtomicU32 = sync.AtomicU32.new(0)
fn on_event() -> () {
COUNTER.fetch_add(1, sync.Ordering.Relaxed)
}
fn read_count() -> u32 {
return COUNTER.load(sync.Ordering.Acquire)
}
Available: AtomicU8, AtomicU16, AtomicU32, AtomicBool.
Ordering values: Relaxed, Acquire, Release, AcqRel, SeqCst.
OnceCell<T>: Single-Assignment Global
use core/sync
static DEVICE_ID: sync.OnceCell<u32> = sync.OnceCell.new()
fn init() -> () {
DEVICE_ID.set(read_chip_id())
}
fn get_id() -> u32 {
return DEVICE_ID.get().unwrap_or(0)
}
set() is only valid once. Subsequent calls are ignored.
Lazy<T>: On-Demand Initialization
use core/sync
static CALIBRATION: sync.Lazy<Calibration> = sync.Lazy.new(|| -> Calibration {
return Calibration.load_from_flash()
})
fn use_calibration() -> () {
let cal = CALIBRATION.get() // initialized on first call
apply(cal)
}
Critical Sections
use core/sync
let guard = sync.CriticalSection.enter()
defer guard.exit()
// interrupts disabled, shared state is safe to access
update_shared_flag()
std/sync: Higher-Level Synchronization
Channels
use std/sync
let ends = sync.channel<u32>(16)? // capacity 16
let tx = ends.tx
let rx = ends.rx
// Producer
tx.send(42)? // blocking send
tx.try_send(42)? // non-blocking, returns Err if full
// Consumer
let value = rx.recv()? // blocking receive
if let Ok(value) = rx.try_recv() {
process(value)
}
// Shutdown
tx.close()
Mutex
use std/sync
let shared: sync.Mutex<List<u8>> = sync.Mutex.new(List.new())?
fn push_byte(byte: u8) -> Result<(), Error> {
let mut guard = shared.lock()?
defer guard.release()
guard.value.push(byte)?
return Ok()
}
Semaphore
use std/sync
let sem = sync.Semaphore.new(0)? // initial count 0
// Signal from ISR or other core
sem.signal()
// Wait in main loop
sem.wait()?
Multicore (RP2040)
use micro/multicore
fn core1_main() -> never {
loop {
// secondary core work
}
}
fn main() -> never {
multicore.launch(1, core1_main)?
loop {
// primary core work
}
}
ISR Rules
ISR code must not block. In an interrupt handler:
- Use atomics and
OnceCell/Lazyreads: ok. - Use
try_sendandtry_recvon channels: ok. - Use
sync.CriticalSectioncarefully: ok if brief. - Never call
recv,send,lock, orwait; these block and are not safe in ISRs.