Adapter Thread Safety Guide#
This package allows callbacks and execute() to run concurrently depending on executor/callback-group setup.
Adapter implementations must be data-race free under concurrent execution.
Rules#
- Protect every mutable shared state with a mutex.
- Split mutexes by responsibility when states are unrelated.
- Do not hold adapter mutex while calling
gateway_->submit()/submit_request(). - Do not hold adapter mutex while doing long I/O (publish, UDS send/recv, service call).
- If multiple mutexes are needed, define and document a strict lock order.
- Prefer lock-free snapshots:
- copy the minimum shared state under lock,
- release lock,
- perform heavy computation,
- write back results under lock.
Recommended Mutex Split#
status_mutex: latest inbound status caches/timestamps.policy_mutex: command-derived mode/flag caches updated fromexecute().result_mutex: derived diagnostic/result caches read by diagnostic callbacks.
Deadlock Risk Checklist#
- Calling
gateway_->submit()while holding adapter mutex: high risk. - Calling
diagnostic_updater::Updater::force_update()while holding a state mutex thatupdate_*_diag()also takes: medium risk. - Nested locks without fixed order: high risk.
Current Built-in Adapters#
DiagAdapter:updater_mutex_: serializesdiagnostic_updater::Updater::force_update().transition_mutex_: protects the transitional-state start timestamp.- Lock order:
updater_mutex_→transition_mutex_.execute()takesupdater_mutex_and callsforce_update(), which synchronously invokesupdate_status(), which takestransition_mutex_. The reverse order never occurs, so this nesting is deadlock-free. Do not introduce a path that takestransition_mutex_first and thenupdater_mutex_.
SubSystemAdapter:state_mutex_: callback-owned state (last_active_control_unit_ids_).
These patterns should be reused by plugin adapter authors.