Files
trail-mate/docs/specification/RUNTIME_CONCURRENCY_SPEC.md
vicliuandGitHub bf7068b02b 0.1.26-alpha release
* refactor: render chat rows from presentation state

* Fix Meshtastic channel sync and add MeshCore CN preset

* Add granular chat notification settings

* Add SD settings backup and restore

* Prepare 0.1.26-alpha release

---------

Co-authored-by: vicliu624 <vicliu@outlook.com>
2026-05-19 16:04:42 +08:00

3.6 KiB

Runtime and Concurrency Specification

This specification defines the concurrency baseline for Trail Mate targets. It exists because ESP32, nRF52, Linux, and tests have different runtime mechanics but must preserve the same ownership model.

Sources of Concurrency

Trail Mate targets may receive work from:

Radio IRQ
Radio RX task or poll loop
Radio TX completion
GPS UART RX
GPS parser task
BLE stack callback
BLE notify queue
UI event loop
LVGL tick/input
GTK main loop
ASCII terminal input loop
HostLink USB/serial RX
Storage write
Config update
Power/sleep/wake
Timer/retry/ACK timeout

Mandatory Rules

ISR code may only defer.

BLE callbacks must not directly mutate app services.

Radio IRQ handlers must not run protocol or business logic.

GPS tasks must not directly update UI.

UI objects may only be updated on the UI owner thread/task.

Storage backends must declare their concurrency model.

Mutable app-service state must have a single owner context.

Cross-thread and cross-task interaction must use one of:

event queue
command queue
immutable snapshot
declared mutex-protected store

Canonical Event Paths

radio_rx:
  path: "radio_irq -> radio_task -> mesh_event_queue -> app_task"
  rule: "IRQ defers; radio_task frames bytes; protocol/app processing runs outside IRQ."

gps_rx:
  path: "uart_irq -> gps_task -> location_event_queue -> app_task"
  rule: "GPS task may parse/normalize but may not directly mutate UI."

ble_write:
  path: "ble_callback -> phone_command_queue -> app_task"
  rule: "BLE stack callback owns transport timing only."

ui_input:
  path: "ui_thread -> presentation_action -> app_command_queue"
  rule: "Renderer translates input into UI actions; app owner executes business mutations."

Forbidden Paths

ble_callback -> ChatService
radio_irq -> MeshSession
gps_task -> lvgl
gtk_worker -> GtkWidget
ui_thread -> blocking storage write
ui_renderer -> direct radio access
ui_renderer -> direct GPS driver access
platform_driver -> direct message policy

UI Thread Only

LVGL:

lv_obj_* may only be called from the LVGL owner task/thread.
Other contexts post UI commands or publish snapshots.

GTK:

GtkWidget mutation may only happen on the GTK main loop.
Workers use main-context invocation, channels, or idle callbacks.

ASCII/TUI:

Terminal output has one renderer owner.
Input and refresh paths must not concurrently write stdout.

Headless:

No renderer owner exists; state is exposed through logs, API, or snapshots.

ISR Policy

ISR code may:

clear interrupt status
record minimal flags
post lightweight ISR-safe events

ISR code must not:

malloc/free
write storage
notify BLE
update UI
encode/decode protobuf
send direct messages
parse GPS sentences
perform crypto

Storage Concurrency

Every storage backend must declare:

single writer or multiple writers
reader model
transaction support
async write support
erase/write blocking behavior
required owner context or mutex

Examples:

ESP32 NVS: blocking, write-limited, usually mutex protected.
nRF52 flash: erase/write expensive, often async, callback-sensitive.
SQLite: transactional with file locks; must avoid UI-thread blocking.

Mutable State Ownership

Every mutable service must have one owner:

ChatService -> app service context
MeshSession -> mesh/app context declared by target
GpsService -> gps/app context declared by target
ConfigService -> app service context
DeviceStatusService -> app service context
UI State -> UI context or presentation owner

Other contexts may send commands, publish events, or consume snapshots.