Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

gba::critical_section Reference

gba::critical_section is a non-copyable, non-movable RAII guard that masks maskable CPU IRQs for the lifetime of the object. It is defined in <gba/critical_section>.

Include

#include <gba/critical_section>

Type summary

namespace gba {
    struct critical_section final {
        critical_section() noexcept;
        ~critical_section() noexcept;

        critical_section(const critical_section&) = delete;
        critical_section& operator=(const critical_section&) = delete;
        critical_section(critical_section&&) = delete;
        critical_section& operator=(critical_section&&) = delete;

        explicit operator bool() const noexcept;
    };
}

Typical usage

Construction saves the current CPSR IRQ-disable bit and disables maskable IRQs. Destruction restores the saved bit:

{
    const gba::critical_section section;
    update_shared_state();
}
// The IRQ masking state from before the scope is restored.

The guard must remain in the scope that it protects. Its deleted copy and move operations prevent transferring the saved CPU state to another object.

Use a critical section for a short update that must be atomic with respect to an IRQ handler:

volatile unsigned int pending_work = 0;

void add_work(unsigned int amount) {
    const gba::critical_section section;
    pending_work += amount;
}

Keep the protected region short. Do not wait for an interrupt, perform a long copy, or do frame-scale work while the guard is alive.

Nesting

Guards can be nested. Destroying an inner guard restores the state that was active when that inner guard was constructed, so it cannot re-enable IRQs while an outer guard is still alive:

{
    const gba::critical_section outer;
    {
        const gba::critical_section inner;
        // IRQs are masked.
    }
    // IRQs are still masked.
}

operator bool

operator bool() always returns true and exists to support an if-condition scope:

if (const gba::critical_section section{}) {
    update_shared_state();
}

It does not report whether masking succeeded and should not be used as an interrupt-state query. A normal block is usually clearer for longer scopes.

Register interaction

The guard masks IRQ acceptance in the CPU’s CPSR. It does not modify gba::reg_ime or the individual enables in gba::reg_ie. Use those registers to configure interrupt delivery; use critical_section to make a short sequence atomic with respect to IRQ handlers.

For example, reg_ime remains enabled while the CPU is masked:

#include <gba/peripherals>

gba::reg_ime = true;
{
    const gba::critical_section section;
    // `gba::reg_ime` is still true, but maskable IRQs are not accepted.
}

Pending interrupts are handled after the guard is destroyed when the normal interrupt conditions are enabled. A critical section does not synchronize DMA memory access.