Prechádzať zdrojové kódy

modified: Actorを追加

Satoshi Yoneda 1 mesiac pred
rodič
commit
80fe01e3e5

+ 35 - 7
main.cpp

@@ -4,6 +4,7 @@
 #include "pico/stdlib.h"
 
 #include "tinykernel/kernel.hpp"
+#include "tinykernel/actor.hpp"
 
 #include "pico/multicore.h"
 
@@ -17,15 +18,40 @@
     } while (0)
 
 // ---------------------------------------------------------------------------
-// Static task (registered at link time via TK_TASK): toggles the onboard LED.
-void task_led(void* arg) {
+// Actor demonstration: Receiver Actor waiting for messages.
+// It consumes 0 CPU cycles while waiting, and immediately wakes up on post().
+enum AppMsgId : uint32_t {
+    MSG_LED_TOGGLE = 1,
+    MSG_PING       = 2,
+};
+
+class LedActor : public tk::Actor<8> {
+protected:
+    void on_message(const tk::Message& msg) override {
+        if (msg.id == MSG_LED_TOGGLE) {
+            bool state = !gpio_get(25);
+            gpio_put(25, state);
+            P("LedActor: received MSG_LED_TOGGLE (seq=%lu) -> LED %s, now=%llu ms\n",
+              (unsigned long)msg.arg, state ? "ON" : "OFF", (unsigned long long)tk::now_ms());
+        } else {
+            P("LedActor: received unknown msg id=%lu, arg=%lu\n",
+              (unsigned long)msg.id, (unsigned long)msg.arg);
+        }
+    }
+};
+
+static LedActor g_led_actor;
+
+// Sender task: periodically posts messages to LedActor
+static void task_sender(void* arg) {
     (void)arg;
+    uint32_t seq = 0;
     for (;;) {
-        gpio_put(25, !gpio_get(25));
-        tk::sleep_ms(200);
+        tk::sleep_ms(500);
+        P("Sender: posting MSG_LED_TOGGLE seq=%lu...\n", (unsigned long)seq);
+        g_led_actor.post(MSG_LED_TOGGLE, seq++);
     }
 }
-TK_TASK(task_led, tk::PRIO_NORMAL)
 
 // ---------------------------------------------------------------------------
 // Static task: FPU self-check. Runs the same deterministic float/double sequence
@@ -100,15 +126,17 @@ int main() {
     gpio_set_dir(25, GPIO_OUT);
 
     printf("tinykernel: registering dynamic tasks...\n");
+    g_led_actor.start(tk::PRIO_NORMAL);
+    auto h_sender = tk::create(task_sender, nullptr, tk::PRIO_NORMAL);
     auto h_hi = tk::create(task_hi, nullptr, tk::PRIO_HIGH);
     auto h_dy = tk::create(task_dying, nullptr, tk::PRIO_LOW);
     auto h_pr = tk::create(task_print, nullptr, tk::PRIO_NORMAL);
-    if (h_hi.slot == 0xFF || h_dy.slot == 0xFF || h_pr.slot == 0xFF) {
+    if (!g_led_actor.handle().is_valid() || !h_sender.is_valid() || !h_hi.is_valid() || !h_dy.is_valid() || !h_pr.is_valid()) {
         printf("tinykernel: task creation FAILED\n");
         for (;;) {
         }
     }
 
-    printf("tinykernel: starting scheduler (preemptive, core M33 only)\n");
+    printf("tinykernel: starting scheduler (preemptive, dual-core M33)\n");
     tk::start();  // never returns
 }

+ 93 - 0
tinykernel/include/tinykernel/actor.hpp

@@ -0,0 +1,93 @@
+#pragma once
+
+#include "kernel.hpp"
+#include <cstdint>
+#include <cstddef>
+
+namespace tk {
+
+struct Message {
+    uint32_t id;
+    uintptr_t arg;
+};
+
+template <size_t QueueSize = 8>
+class Actor {
+    static_assert(QueueSize > 0, "QueueSize must be greater than 0");
+
+public:
+    Actor() = default;
+    virtual ~Actor() = default;
+
+    // タスクとして起動する。tk::start() より前に呼ぶ。
+    bool start(uint32_t prio = PRIO_NORMAL) {
+        handle_ = tk::create(task_entry, this, prio);
+        return handle_.is_valid();
+    }
+
+    // メッセージを送信する(非同期、スレッドセーフ、割り込みセーフ)
+    bool post(const Message& msg) {
+        uint32_t save = detail::sched_lock();
+        if (count_ >= QueueSize) {
+            detail::sched_unlock(save);
+            return false;
+        }
+        queue_[tail_] = msg;
+        tail_ = (tail_ + 1) % QueueSize;
+        count_++;
+        if (waiting_) {
+            waiting_ = false;
+            detail::wake_task_locked(handle_);
+        }
+        detail::sched_unlock(save);
+        return true;
+    }
+
+    bool post(uint32_t id, uintptr_t arg = 0) {
+        return post(Message{id, arg});
+    }
+
+    TaskHandle handle() const { return handle_; }
+
+protected:
+    // 派生クラスでオーバーライドしてメッセージを処理する
+    virtual void on_message(const Message& msg) = 0;
+
+private:
+    static void task_entry(void* arg) {
+        static_cast<Actor<QueueSize>*>(arg)->event_loop();
+    }
+
+    void event_loop() {
+        for (;;) {
+            Message msg{};
+            uint32_t save = detail::sched_lock();
+            while (count_ == 0) {
+                waiting_ = true;
+                detail::block_current_task_locked();
+                detail::sched_unlock(save);
+                // コンテキストスイッチされ、他のタスクが走る。
+                // wake_task_locked で起こされたらここに戻る。
+                save = detail::sched_lock();
+            }
+            msg = queue_[head_];
+            head_ = (head_ + 1) % QueueSize;
+            count_--;
+            waiting_ = false;
+            detail::sched_unlock(save);
+
+            // メッセージディスパッチ(ロック解除状態で実行)
+            on_message(msg);
+        }
+    }
+
+    Message queue_[QueueSize]{};
+    size_t head_{0};
+    size_t tail_{0};
+    size_t count_{0};
+    bool waiting_{false};
+    TaskHandle handle_{};
+};
+
+}  // namespace tk
+

+ 6 - 1
tinykernel/include/tinykernel/kernel.hpp

@@ -38,7 +38,8 @@ enum Prio : uint32_t {
 };
 
 struct TaskHandle {
-    uint8_t slot;  // 0xFF on failure
+    uint8_t slot{0xFF};  // 0xFF on failure
+    bool is_valid() const { return slot != 0xFF; }
 };
 
 // Create a task. Must be called before tk::start() (Phase 1 restriction).
@@ -61,6 +62,10 @@ void unlock(bool was_enabled);
 
 namespace detail {
 void static_register(const char* name, TaskFn fn, uint32_t prio);
+uint32_t sched_lock();
+void sched_unlock(uint32_t save);
+void block_current_task_locked();
+void wake_task_locked(TaskHandle handle);
 }  // namespace detail
 
 }  // namespace tk

+ 25 - 1
tinykernel/src/scheduler.cpp

@@ -17,7 +17,7 @@ namespace {
 
 constexpr uint32_t TK_NUM_CORES = 2;
 
-enum class State : uint8_t { Free = 0, Ready, Running, Sleeping, Terminated };
+enum class State : uint8_t { Free = 0, Ready, Running, Sleeping, Blocked, Terminated };
 
 struct TaskSlot {
     State state;
@@ -308,3 +308,27 @@ bool tk::lock() {
 void tk::unlock(bool was_enabled) {
     sched_unlock(was_enabled ? 0 : 1);
 }
+
+uint32_t tk::detail::sched_lock() {
+    return ::sched_lock();
+}
+
+void tk::detail::sched_unlock(uint32_t save) {
+    ::sched_unlock(save);
+}
+
+void tk::detail::block_current_task_locked() {
+    uint32_t core = get_core_num();
+    TaskSlot& cur = g_slots[g_cur[core]];
+    cur.state = State::Blocked;
+    tk_port_trigger_pendsv();
+}
+
+void tk::detail::wake_task_locked(TaskHandle handle) {
+    if (handle.slot >= TK_MAX_TASKS) return;
+    TaskSlot& s = g_slots[handle.slot];
+    if (s.state == State::Blocked) {
+        s.state = State::Ready;
+        tk_port_trigger_pendsv();
+    }
+}