Skip to content
DeguiLiuPublic

About

C++17 active-object/HSM event framework. One header set for RT-Thread, FreeRTOS, and Linux. Bounded (fixed-capacity, no heap), detectable (result codes, counters, breaker levels), recoverable (defined conditions).

Topics

Resources

Stars

98 stars

Watchers

0 watching

Forks

Latest commit

 

History

143 Commits

Folders and files

Repository files navigation

中文 | English

coact

CI License: MIT

coact (Cooperative Active-object framework) is a header-first C++17 event framework whose active-object and hierarchical state machine model follows QP/C++ by Quantum Leaps. It targets single-core MCUs running RT-Thread or FreeRTOS, and the same headers build and run on Linux and Windows hosts through the Posix and Windows PALs (host is a test reference, not a product target). Producers submit events from task or ISR context; one dispatcher thread delivers them to active objects whose behaviour is a hierarchical state machine. Events replace threads, transition tables are constexpr, and nothing allocates at run time. The submission, staging, admission, breaker and PAL layers are coact's own.

Three properties are structural here, not aspirations:

Property Where it lives in the tree How it is checked
Bounded every queue, pool and batch size is a compile-time constant; no heap, no new/delete, no dynamic registration worst-case flash/RAM and the longest dispatch batch are fixed at link time (test/renode/footprint.sh)
Detectable SubmitResult codes, Monitor counters, Breaker::level(), the fatal_assert handler a failure is visible where it happens, with the AO and partition it belongs to
Recoverable one breaker state machine per AO (Normal, BrokenL1, BrokenL2, Safe, Recovering) over static HSMs recovery has explicit conditions: cooldown, sustained low watermark, successful probe, consecutive healthy windows (examples/safety_supervisor_demo.cpp)

coact supplies the mechanisms; the board supplies the evidence. Partition watermarks, watchdog progress and reserve exhaustion are sampled by BSP code that calls the BreakerBank inputs, as the supervisor example shows.

Why coact

  • C++17 only, no code generator. Transitions are ordinary constexpr tables in your translation unit; no modelling tool sits in the build.
  • Single core is the design point. SingleCoreCriticalRing plus an injected CriticalSection (irq mask on RT-Thread, task critical section / ISR mask on FreeRTOS) keeps the ISR path lock-free and libatomic-free; host SMP is a test reference, not the product target.
  • Fixed capacity, zero hot-path allocation. A fixed-size EventPool recycled by ref-count lets one event fan out safely (-fno-exceptions -fno-rtti).
  • RT-Thread and FreeRTOS are first-class targets, not ports. Both PALs use caller-provided static resources and never allocate; Linux and Windows hosts build from the same headers through the same PAL boundary.
  • Critical-High staging reservation (this branch): kHighCriticalReserve keeps High-partition cells free for critical events, plus a ReservedNormal lane.
  • Deterministic wakeups. The dispatcher wakes only when idle and a drain-check closes the missed-wakeup window; try_submit_from_isr never blocks.
  • Back-pressure, not silent drops. A breaker degrades under overload; the low-priority partition ages out instead of starving.
  • Coroutines included (coact/coro/): stackful coroutines, CoroSem, and stack guard/watermark diagnostics.

Why C++17

C can implement everything coact does. The difference is who enforces the constraints: in C, capacity relationships, resource ownership, handle types and cleanup pairing live in comments, naming prefixes and review checklists; in C++17, part of them lives in the type system and in compile-time assertions. coact uses only that subset: no exceptions, no RTTI, no heap, no code generator. Against an equivalent C implementation the cost is about 1 to 2 KiB more flash and roughly the same RAM. The measurements, the cases where C wins and the limits of the approach are in C++17 与 C 的能力差异、成本与使用边界 (Chinese).

Topology and layering

flowchart LR
    classDef prod fill:#FFE0B2,stroke:#E65100,color:#000
    classDef core fill:#BBDEFB,stroke:#0D47A1,color:#000
    classDef app fill:#E1BEE7,stroke:#4A148C,color:#000
    PR["producer<br/>task / ISR submit"]:::prod
    C["Coordinator<br/>submit pipeline"]:::core
    S["Staging<br/>High · Normal · Low"]:::core
    D["Dispatcher<br/>single thread"]:::core
    A["Active Object<br/>+ HSM"]:::app
    P[("EventPool<br/>fixed · ref-counted")]:::prod
    PR --> C --> S --> D --> A --> P
    P -.->|recycle / alloc| PR
Loading
flowchart TB
    classDef app fill:#E1BEE7,stroke:#4A148C,color:#000
    classDef core fill:#BBDEFB,stroke:#0D47A1,color:#000
    classDef pal fill:#FFE0B2,stroke:#E65100,color:#000
    L4["application<br/>Active Object · HSM · Context"]:::app
    L3["core/<br/>Coordinator · Dispatcher · Runtime"]:::core
    L2["blocks<br/>staging · event/pool · queue · monitor · policy · coro"]:::core
    L1["pal/<br/>RtThread · Posix · Windows"]:::pal
    L4 --> L3 --> L2 --> L1
Loading

Quick start (host)

cmake -B build -S . -DCMAKE_BUILD_TYPE=RelWithDebInfo && cmake --build build -j && ctest --test-dir build
#include "coact/runtime.hpp"
#include "coact/pal_posix.hpp"
coact::pal::Posix pal;
coact::Runtime<coact::DefaultConfig, coact::pal::Posix> rt(pal);
rt.bind(&my_ao);          // my_ao : coact::Ao<Ctx, Hsm, Traits>
rt.initialize();
rt.start();

On RT-Thread include the same headers, select coact/pal_rtthread.hpp and compile src/core/pal_rtthread.cpp into the BSP — the PAL uses caller-provided static resources and never allocates.

On FreeRTOS select coact/pal_freertos.hpp and compile src/core/pal_freertos.cpp; FreeRtosResources owns the dispatcher/worker stacks and every kernel object. The port requires a single-core build with static allocation enabled; Cortex-M0/M0+ is not supported.

Modules

event pool queue hsm ao staging monitor policy coro pal span, with coordinator / dispatcher / runtime as the assembly (docs/interface_contract.md).

Testing

Host targets pass under ctest and are TSan-clean on the pool/dispatcher paths; CI adds ASan/UBSan and a Windows MSVC job, and a weekly or on-demand renode job runs the Cortex-M4 fixtures below. Brought up on RT-Thread under qemu-vexpress-a9 (single core) and on FreeRTOS under Renode (Cortex-M4 running the CM3 port), including a fault-injection fixture where IWDG resets a task that masks interrupts and spins.

Credits

The active-object execution model, the pooled-event and reference-count semantics, and the dispatcher loop follow QP/C++ by Quantum Leaps. Quantum Leaps ships SafeQP/C++ (commercial) with certification kits for IEC 61508 and ISO 26262; coact is not a certified product and carries no certification artifacts. coact's own work is the submission pipeline, three-partition staging, admission and breaker policies, the PAL abstraction and the single-core lock-free queues.

License

MIT — see LICENSE.

About

C++17 active-object/HSM event framework. One header set for RT-Thread, FreeRTOS, and Linux. Bounded (fixed-capacity, no heap), detectable (result codes, counters, breaker levels), recoverable (defined conditions).

Topics

Resources

Stars

98 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages