# event_loop
**Repository Path**: john_5/event_loop
## Basic Information
- **Project Name**: event_loop
- **Description**: **Event Loop** 是面向 RT-Thread 的软件包,用于在应用层调度**延迟回调**。待执行项保存在**定长延迟表**中;由**单次软定时器**推进时间;到期的任务通过**消息队列**投递,在专用线程 `**evt_loop`** 上执行
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-04-09
- **Last Updated**: 2026-04-13
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Event Loop
中文说明:[README.md](./README.md)
## 1. Introduction
**Event Loop** is an RT-Thread software package that schedules **delayed callbacks** in user space. Pending work is stored in a **fixed-size delay table**; a **one-shot soft timer** drives time progression; **due** jobs are sent through a **message queue** and executed on a dedicated **`evt_loop` thread** (so callbacks do not run in the timer daemon context).
Typical use: defer UI/state-machine work, staggered retries, or periodic-style chains built from `EVT_LOOP_PUSH` in the callback.
The package is enabled with **`PKG_USING_EVENT_LOOP`**. Metadata is in `package.json`; build integration uses `Kconfig` and `SConscript`.
## 2. Features
- **Delayed dispatch** — `evt_loop_push_delayed()` / macro **`EVT_LOOP_PUSH(func, args, delay_ms)`** (`delay_ms <= 1` is treated as immediate via the message queue).
- **Cancel** — **`EVT_LOOP_REMOVE(func)`** removes **all** pending delayed entries for that function pointer; **`EVT_LOOP_REMOVE_WITH_ARGS(func, args)`** removes entries matching both (see header comments).
- **Thread + MQ** — Callbacks run on the **`evt_loop`** thread; the queue depth is configurable (`EVENT_LOOP_MSGQ_DEPTH`).
- **Single soft one-shot timer** — One `RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER` instance; next expiry is the minimum remaining delay in the table.
- **Concurrency** — Delay table updates are protected by a **mutex**; re-arm logic handles RT-Thread’s **`RT_TIMER_FLAG_PROCESSING`** restriction inside the soft-timer callback (`rt_timer_stop` before reprogramming, deferred `rt_timer_start` via the same message queue when required).
- **Optional sample** — `EVENT_LOOP_USING_SAMPLES` builds `event_loop_test.c` and exports MSH command **`evt_loop_test`** (requires FinSH/MSH).
## 3. Architecture (brief)
```
[ Any thread ] --push_delayed/remove--> [ delay table + mutex ]
|
soft timer (one-shot) ----+--> apply elapsed --> rt_mq_send (due)
|
evt_loop thread <----------+---- rt_mq_recv --> user callback (func, args)
```
The following **sequence diagram** sketches Push / Remove / timeout delivery under **one-shot soft-timer** mode (aligned with `event_loop.c`: delay table + soft timer + message queue). “Lost time” in the figure matches **`delay_diff` / elapsed** calibration in the implementation.
```mermaid
sequenceDiagram
actor User as Event Pusher
Base Timer
participant Push as Push Event
Core Operations
participant Remove as Remove Event
Core Operations
participant TimerCore as Timer Core
Timer Driver
participant EventArray as Event Array
Event Array
participant Task as Business Task
Target Task
rect rgb(240,248,255)
Note over User,Task: 🏷️ Timer one-shot mode — full workflow
end
%% --- Path 1: Push event flow ---
User->>Push: Trigger push event (point A/C)
Push->>EventArray: 1. Find free slot & write data
Note over Push: Calculate Lost_Timer
System tick - last StartTime
Push->>TimerCore: 2. Stop timer & apply calibration
TimerCore->>EventArray: 3. Traverse array & subtract elapsed time
TimerCore->>EventArray: 4. Find min delay & restart timer
Note over TimerCore: Set new StartTime
Start one-shot timer
%% --- Path 2: Remove event flow ---
alt Cancel operation (point E/F)
User->>Remove: Trigger remove event
Remove->>EventArray: 1. Find & clear target event
Note over Remove: Calculate Lost_Timer
System tick - last StartTime
Remove->>TimerCore: 2. Stop timer & apply calibration
TimerCore->>EventArray: 3. Traverse array & subtract elapsed time
alt Array not empty
TimerCore->>TimerCore: 4. Recalculate next nearest timeout
TimerCore->>TimerCore: 5. Restart timer
else
TimerCore->>TimerCore: 5. Stop timer
No pending events
end
end
%% --- Timer fire and task execution ---
TimerCore-->>Task: 🔔 Timer fire / timeout
Note over TimerCore,Task: Check timeout
Execute expired tasks
Task-->>User: 📤 Final execution result
```
## 4. API
Include **`event_loop.h`**.
| Symbol | Role |
|--------|------|
| `EVT_LOOP_PUSH(pfunc, pargs, delay_ms)` | Queue a delayed call; `pfunc` is `void (*)(void *)`. |
| `EVT_LOOP_REMOVE(pfunc)` | Remove **all** table slots whose function pointer equals `pfunc`. |
| `EVT_LOOP_REMOVE_WITH_ARGS(pfunc, pargs)` | Remove slots matching `pfunc` and `pargs` (or all `pfunc` if `pargs` matches the macro’s rules — see implementation). |
| `evt_loop_push_delayed()` / `evt_loop_remove_delayed()` | Underlying C API. |
Initialization is **`INIT_APP_EXPORT(_evt_loop_init)`**; no extra call is required after boot.
## 5. Directory layout
```
event_loop/
├── README.md # This file
├── README_zh.md # Chinese readme
├── inc/
│ └── event_loop.h # Public API
├── src/
│ └── event_loop.c # Implementation
├── samples/
│ └── event_loop_test.c # Optional MSH demo
├── Kconfig # PKG_USING_EVENT_LOOP and options
└── SConscript # DefineGroup, CPPPATH
```
## 6. Dependencies (Kconfig)
Enabling **`PKG_USING_EVENT_LOOP`** selects:
- `RT_USING_MESSAGEQUEUE`
- `RT_USING_MUTEX`
- `RT_USING_TIMER_SOFT`
Configurable options:
| Option | Meaning |
|--------|---------|
| `EVENT_LOOP_MAX_EVENT_CNT` | Maximum concurrent delayed slots (default 32). |
| `EVENT_LOOP_MSGQ_DEPTH` | Message queue depth (default 15). |
| `EVENT_LOOP_THREAD_STACK_SIZE` | `evt_loop` thread stack (default 3072). |
| `EVENT_LOOP_THREAD_PRIORITY` | Priority (default 12). **Must be strictly greater than `RT_TIMER_THREAD_PRIO`** (numerically larger = lower CPU priority than the soft-timer thread). |
| `EVENT_LOOP_USING_SAMPLES` | Build the sample (default off in Kconfig). |
## 7. Get started
### 7.1 menuconfig
```
RT-Thread online packages
system packages --->
[*] Event loop (delayed dispatch: mq + soft one-shottimer) --->
(32) Maximum delayed slots in table
(15) Message queue depth (immediate + due callbacks)
(3072) Event loop thread stack size (bytes)
(12) Event loop thread priority (smaller = higher5
[*] Build event_loop sample (event_loop_test.c)
```
1. Open **menuconfig** from your BSP.
2. Enable **`PKG_USING_EVENT_LOOP`** (*Event loop (delayed dispatch: mq + soft one-shot timer)*).
3. Adjust stack, priority, table size, and MQ depth as needed.
4. Optionally enable **`EVENT_LOOP_USING_SAMPLES`** for `evt_loop_test`.
5. Save and confirm `rtconfig.h` defines `PKG_USING_EVENT_LOOP`.
### 7.2 Build
From the BSP root, run `scons` (or your usual RT-Thread build).
### 7.3 Application code
```c
#include "event_loop.h"
static void my_job(void *arg)
{
/* ... */
EVT_LOOP_REMOVE(my_job);
/* optional: EVT_LOOP_PUSH(my_job, arg, delay_ms); */
}
/* from any thread after boot */
EVT_LOOP_PUSH(my_job, (void *)ctx, 100);
/* from any thread after boot: cancel all pending delayed calls for my_job */
EVT_LOOP_REMOVE(my_job);
/* from any thread after boot: cancel delayed call(s) matching this function pointer and args */
EVT_LOOP_REMOVE_WITH_ARGS(my_job, (void *)1);
```
### Sample animation (`evt_loop_test`)
With **`EVENT_LOOP_USING_SAMPLES`** enabled, this is a short capture of running **`evt_loop_test`** from MSH:

## 8. Notes
- **Priority** — If compile fails with the static assert / `#error` on priority, raise `EVENT_LOOP_THREAD_PRIORITY` above `RT_TIMER_THREAD_PRIO` in `rtconfig.h`.
- **Table full** — If the delay table is exhausted, a warning is logged and the push is dropped; size with `EVENT_LOOP_MAX_EVENT_CNT`.
- **Same function, multiple slots** — `EVT_LOOP_REMOVE(func)` clears every matching slot; design callbacks so this matches your intent.
## 9. License
Apache License 2.0 (SPDX in sources and `package.json`).
## 10. Maintainer / repository
See **`package.json`** for author, repository URL, and version/site entries.