.. include:: Firmware: Fixing Interrupt Concurrency Bugs (Spinlock) ====================================================== .. topic:: See also * :doc:`../../spinlock/index` Interrupts Are Special ---------------------- * For example: 7 ("sensor burning") beats 42 (timer) * |longrightarrow| Best to treat ISRs as "parallel" * |longrightarrow| Trust nobody Low Hanging Fruit ----------------- In ``FSM::start()``: .. code-block:: c++ case State::INIT: _interval_timer.start(Why::TimeSpec(0,1), _interval); Why::IRQ::connect(_sensor_burning_irq, [this](int irqnum){_sensor_burning();}); Why::IRQ::connect(_operator_button_irq, [this](int irqnum){_operator_button_pressed();}); _sensor.turn_on(); _state = State::RUNNING; break; * What if timer expires before state is set to ``RUNNING``? * What if we measure before sensor has been turned on? * |longrightarrow| Look in ``FSM::_timer_tick()`` Subtle: Interrupt Nesting (7 Beats Timer) ----------------------------------------- * Sensor blows up right at the same time a "soft overheat" is detected * Timer tick interrupted by higher priority IRQ * State change to ``SENSORBURNING`` * Timer tick continues * |longrightarrow| Overwrites ``SENSORBURNING`` with ``OVERTEMP`` * **"State Change Overlap"** .. list-table:: :align: left :header-rows: 1 * * ISR: *Timer tick* * ISR: *Sensor burning* * * .. code-block:: c++ case State::RUNNING: { double temp = _sensor.get_temperature(); if (temp > _soft_critical_temp) { _cooling.set_state(true); . . . . . . _soft_alarm.set_state(true); _state = State::OVERTEMP; } ... break; * .. code-block:: c++ . . . . . case State::RUNNING: _sensor.turn_off(); _cooling.set_state(false); _state = State::SENSORBURNING; _hard_alarm.set_state(true); break; . . . . . Effects Of Unprotected Interrupt Nesting ---------------------------------------- .. image:: fsm-parallel.svg Protecting Against Unwanted Interrupt Nesting --------------------------------------------- .. topic:: See also * :doc:`../../spinlock/index` * Turn off interrupts before *critical section* is entered * But: there is a better too that does more * ... *and also works on a multicore CPU* * |longrightarrow| :doc:`Spinlock <../../spinlock/index>` Critical Section? ----------------- * Look in state machine * First: ``FSM::_timer_tick()`` * What is critical? Remember problem: in ``RUNNING``, we got interrupted. * Here's the relevant code from :doc:`../bare-metal-initial/fsm` .. code-block:: c++ switch (_state) { ... case State::RUNNING: { double temp = _sensor.get_temperature(); if (temp > _soft_critical_temp) { _cooling.set_state(true); _soft_alarm.set_state(true); _state = State::OVERTEMP; } else if (temp > _high_temp) _cooling.set_state(true); else if (temp < _low_temp) _cooling.set_state(false); break; } ... } Updated FSM: Spinlock --------------------- .. literalinclude:: code/fsm.h :caption: :download:`code/fsm.h` :language: c++ .. literalinclude:: code/fsm.cpp :caption: :download:`code/fsm.cpp` :language: c++