tinykernel
Loading...
Searching...
No Matches
actor.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "kernel.hpp"
4#include <cstdint>
5#include <cstddef>
6#include <type_traits>
7#include <utility>
8
9namespace tk {
10
14struct Message {
15 uint32_t id;
16 uintptr_t arg;
17};
18
28template <typename MsgType, size_t MaxSubscribers = 4>
29class EventHub {
30 struct Subscriber {
31 void* actor{nullptr};
32 bool (*post_fn)(void*, const MsgType&){nullptr};
33 };
34
35 static inline Subscriber subs_[MaxSubscribers]{};
36 static inline size_t count_{0};
37
38public:
45 static bool subscribe(void* actor, bool (*post_fn)(void*, const MsgType&)) {
46 uint32_t save = detail::sched_lock();
47 if (count_ >= MaxSubscribers) {
49 return false;
50 }
51 for (size_t i = 0; i < count_; ++i) {
52 if (subs_[i].actor == actor) {
54 return true; // 登録済み
55 }
56 }
57 subs_[count_++] = {actor, post_fn};
59 return true;
60 }
61
67 static bool unsubscribe(void* actor) {
68 uint32_t save = detail::sched_lock();
69 for (size_t i = 0; i < count_; ++i) {
70 if (subs_[i].actor == actor) {
71 for (size_t j = i; j + 1 < count_; ++j) {
72 subs_[j] = subs_[j + 1];
73 }
74 count_--;
76 return true;
77 }
78 }
80 return false;
81 }
82
89 static size_t publish(const MsgType& msg) {
90 Subscriber local_subs[MaxSubscribers];
91 size_t n = 0;
92 {
93 uint32_t save = detail::sched_lock();
94 n = count_;
95 for (size_t i = 0; i < n; ++i) {
96 local_subs[i] = subs_[i];
97 }
99 }
100
101 size_t delivered = 0;
102 for (size_t i = 0; i < n; ++i) {
103 if (local_subs[i].post_fn && local_subs[i].post_fn(local_subs[i].actor, msg)) {
104 delivered++;
105 }
106 }
107 return delivered;
108 }
109
111 static size_t subscriber_count() {
112 uint32_t save = detail::sched_lock();
113 size_t n = count_;
115 return n;
116 }
117};
118
134template <typename MsgType>
135inline size_t publish(const MsgType& msg) {
136 return EventHub<MsgType>::publish(msg);
137}
138
146inline size_t publish(uint32_t id, uintptr_t arg = 0) {
147 return publish(Message{id, arg});
148}
149
173template <typename MsgType = Message, size_t QueueSize = 8>
174class Actor {
175 static_assert(QueueSize > 0, "QueueSize must be greater than 0");
176
177public:
178 Actor() = default;
179 virtual ~Actor() {
181 }
182
191 bool start(uint32_t prio = PRIO_NORMAL) {
192 EventHub<MsgType>::subscribe(this, [](void* obj, const MsgType& m) {
193 return static_cast<Actor<MsgType, QueueSize>*>(obj)->post(m);
194 });
195 handle_ = tk::create(task_entry, this, prio);
196 return handle_.is_valid();
197 }
198
207 bool post(const MsgType& msg) {
208 uint32_t save = detail::sched_lock();
209 if (count_ >= QueueSize) {
211 return false;
212 }
213 queue_[tail_] = msg;
214 tail_ = (tail_ + 1) % QueueSize;
215 count_++;
216 if (waiting_) {
217 waiting_ = false;
219 }
221 return true;
222 }
223
230 bool post(MsgType&& msg) {
231 uint32_t save = detail::sched_lock();
232 if (count_ >= QueueSize) {
234 return false;
235 }
236 queue_[tail_] = std::move(msg);
237 tail_ = (tail_ + 1) % QueueSize;
238 count_++;
239 if (waiting_) {
240 waiting_ = false;
242 }
244 return true;
245 }
246
254 template <typename U = MsgType>
255 typename std::enable_if<std::is_same<U, Message>::value, bool>::type
256 post(uint32_t id, uintptr_t arg = 0) {
257 return post(Message{id, arg});
258 }
259
261 TaskHandle handle() const { return handle_; }
262
263protected:
270 virtual void on_start() {}
271
280 virtual void on_message(const MsgType& msg) = 0;
281
282private:
283 static void task_entry(void* arg) {
284 static_cast<Actor<MsgType, QueueSize>*>(arg)->event_loop();
285 }
286
287 void event_loop() {
288 on_start();
289 for (;;) {
290 MsgType msg{};
291 uint32_t save = detail::sched_lock();
292 while (count_ == 0) {
293 waiting_ = true;
296 // コンテキストスイッチされ、他のタスクが走る。
297 // wake_task_locked で起こされたらここに戻る。
298 save = detail::sched_lock();
299 }
300 msg = std::move(queue_[head_]);
301 head_ = (head_ + 1) % QueueSize;
302 count_--;
303 waiting_ = false;
305
306 // メッセージディスパッチ(ロック解除状態で実行)
307 on_message(msg);
308 }
309 }
310
311 MsgType queue_[QueueSize]{};
312 size_t head_{0};
313 size_t tail_{0};
314 size_t count_{0};
315 bool waiting_{false};
316 TaskHandle handle_{};
317};
318
319} // namespace tk
virtual void on_message(const MsgType &msg)=0
メッセージ受信時に呼ばれるハンドラ関数
bool post(const MsgType &msg)
この Actor のメッセージキューへ直接メッセージを送信します (コピー渡し)
Definition actor.hpp:207
std::enable_if< std::is_same< U, Message >::value, bool >::type post(uint32_t id, uintptr_t arg=0)
tk::Message 型の場合のコンビニエンス送信関数
Definition actor.hpp:256
TaskHandle handle() const
Definition actor.hpp:261
Actor()=default
bool post(MsgType &&msg)
この Actor のメッセージキューへ直接メッセージを送信します (ムーブ渡し)
Definition actor.hpp:230
virtual ~Actor()
Definition actor.hpp:179
bool start(uint32_t prio=PRIO_NORMAL)
Actor をタスクとして生成し、EventHub へ自動登録します
Definition actor.hpp:191
virtual void on_start()
タスク起動時、イベントループ突入直前に1度だけ呼ばれる初期化関数
Definition actor.hpp:270
メッセージ型ごとの超軽量 Pub/Sub ディスパッチャ
Definition actor.hpp:29
static bool unsubscribe(void *actor)
Actor の購読登録を解除します
Definition actor.hpp:67
static size_t publish(const MsgType &msg)
このメッセージ型を購読しているすべての Actor へ同報配信します
Definition actor.hpp:89
static bool subscribe(void *actor, bool(*post_fn)(void *, const MsgType &))
Actor をこのメッセージ型の購読者として登録します
Definition actor.hpp:45
static size_t subscriber_count()
Definition actor.hpp:111
void block_current_task_locked()
Definition scheduler.cpp:333
uint32_t sched_lock()
Definition scheduler.cpp:325
void sched_unlock(uint32_t save)
Definition scheduler.cpp:329
void wake_task_locked(TaskHandle handle)
Definition scheduler.cpp:340
Definition actor.hpp:9
size_t publish(const MsgType &msg)
メッセージの型を指定して、購読しているすべての Actor へ同報配信します
Definition actor.hpp:135
@ PRIO_NORMAL
通常優先度 (デフォルト)
Definition kernel.hpp:42
TaskHandle create(TaskFn fn, void *arg=nullptr, uint32_t prio=PRIO_NORMAL)
タスクを生成・登録します
Definition scheduler.cpp:204
デフォルトの軽量メッセージ構造体
Definition actor.hpp:14
uintptr_t arg
引数 (ポインタまたは整数値)
Definition actor.hpp:16
uint32_t id
メッセージ識別子 / コマンドID
Definition actor.hpp:15
タスクの識別ハンドル
Definition kernel.hpp:49