Re-entrancy
⚠️ The design and implementation chapter is outdated ⚠️
Where can re-entrancy occur? Turns out that with global singletons it can occur about anywhere; you don't need interrupts (preemption) to cause re-entrancy. See below:
#![allow(unused)] fn main() { extern crate defmt; let x = 0u8; defmt::info!("The answer is {=?}!", x /*: Struct */); }
As you have seen before this will first send the string index of "The answer is {=?}!" and then call x
's Format::format
method.
The re-entrancy issue arises if the Format
implementation calls a logging macro:
#![allow(unused)] fn main() { extern crate defmt; struct X; impl defmt::Format for X { fn format(&self, f: defmt::Formatter) { // ^ this is a handle to the global logger defmt::info!("Hello!"); // .. } } }
f
is a handle to the global logger.
The info!
call inside the format
method is trying to access the global logger again.
If info!
succeeds then you have two exclusive handles (Formatter
) to the logger and that's UB.
If info!
uses a spinlock to access the logger then this will deadlock.