8个不稳定版本 (3个破坏性更新)
新功能 0.5.3 | 2024年8月23日 |
---|---|
0.5.2 | 2024年8月20日 |
0.5.1 |
|
0.5.0 | 2024年4月30日 |
0.2.0 | 2024年1月4日 |
#180 in 算法
每月2,418次下载
115KB
2.5K SLoC
rex-sm 👑
设计特点
- 将状态机做成可复用的模块单元
- 使用其他状态机的状态机知道它们可以使用哪些,但正在使用它们的状态机不知道使用它们的状态机
- 无任务/线程
- 并发处理多个状态机
- 触发其他状态机,等待其完成,然后继续
- 创建/删除计时器
- 向状态机组外发送通知
- 向自身发送事件
- 从状态机组外接受事件
- 鼓励将状态机实现表示为
(<State>, <Event>)
格式
灵感来源
概述
StateMachine
首先在 StateMachineManager
中注册,我将简单地称之为 Manager
。每次调用 Manager::cycle()
都处理一个单个事件。一个事件对应于运行一个状态机。 Manager
访问 Controller
的内容并对其进行操作。单个 Controller
在 Manager
注册的所有状态机之间共享。
有两种类型的事件 UserEvent
和 SystemEvent
。 UserEvent
传递给 StateMachine::cycle()
,而 SystemEvent
则不传递。 StateMachine::cycle()
接受一个 &mut Controller
和一个 UserEvent
。 StateMachine
使用 Controller
中的函数来添加/删除事件到事件队列;除了与计时器相关的函数之外,所有函数都这样做。 SystemEvent
被管理器消费,并用于修改 Controller
的内部或向状态机组外发送数据或通知。
基于节点的状态机管理器(开发中)
目标
- 将状态机输入处理从特定 当前枚举状态 解耦
- 表示所有输入都流向同一接收器(管理员的
signal_queue
);这允许电梯和过渡处理 统一,从而避免通过Box<dyn Signal>
导致的类型不透明。
在实际应用中,设计应保证最多只有三条消息流连接到特定的状态机。
I/O
- 一个
Input
(处理外部和内部事件)
Signal {
id: StateId<K>,
input: I,
}
两个输出
Signal
输出(事件意味着要作为其他状态机的输入处理)Notification
输出(事件意味着要由不是由给定signal_queue
提供的状态机处理的任何事物处理)
StateMachineManager
拥有
- 状态存储层(使用
StateStore
) - 输入事件流
- 状态机处理器
StateMachineManager
负责以下任务
- 将
Signal
路由到相应的状态机 - 将
ProcessorContext
注入状态机:这个操作允许状态机并发循环
StateStore
负责以下任务
- 插入和更新各种状态层次结构
- 通过在
Arc<Mutex<_>>
容器中保持所有节点树来并发执行操作。
这允许 StateStore
存储能够
- 通过增加
Arc
计数并插入每个子节点的新条目来通过新的DashMap
键插入创建多个索引,指向相同的树 - 允许每个状态树具有独立的内部可变性,解耦无关的状态与资源竞争
基于节点的 TimeoutManager
(开发中)
考虑事项
- 每个
StateId<K>
只有一个活动计时器,状态机不需要跟踪发送到通知的Operation::Set(Instant::now())
。因此,所有计时器都应该可以通过StateId<K>
索引。 - 同一
StateId<K>
的新Operation::Set
应该覆盖旧计时器。 - 超时应发出与相关状态机相关的
Signal
。
方法
TimeoutManager
实现了一种每滴答轮询的方法来解决超时TimeoutManager
接受两种类型的输入,设置和取消(超时)- 超时存储在
TimeoutLedger
中 TimeoutLedger
包含一个BTreeMap
,按Instant
索引 ID,以及一个HashMap
,按Instant
索引 ID。这种双重索引允许超时取消操作通过,而无需提供要删除的Instant
。
依赖关系
~9-17MB
~200K SLoC