Pārlūkot izejas kodu

Add: セマフォ/ミューテックス

Satoshi Yoneda 1 mēnesi atpakaļ
vecāks
revīzija
a52d9590b9

+ 7 - 0
README.md

@@ -0,0 +1,7 @@
+# Raspberry Pi Pico 2(RP2350)用のプリエンプティブマルチタスクミニカーネル
+
+- デュアルコア対応カーネル(Tk)
+- タスク間通信(tk::Actor)
+- タスク同期プリミティブ(tk::Semaphore、tk::Mutex)
+
+実装済み

+ 59 - 44
main.cpp

@@ -5,6 +5,7 @@
 
 #include "tinykernel/kernel.hpp"
 #include "tinykernel/actor.hpp"
+#include "tinykernel/sync.hpp"
 
 #include "pico/multicore.h"
 
@@ -27,6 +28,14 @@ enum AppMsgId : uint32_t {
 
 class LedActor : public tk::Actor<8> {
 protected:
+    void on_start() override {
+        // Hardware initialization within task context
+        gpio_init(25);
+        gpio_set_dir(25, GPIO_OUT);
+        gpio_put(25, 0);
+        P("LedActor: initialized GPIO 25 in on_start()\n");
+    }
+
     void on_message(const tk::Message& msg) override {
         if (msg.id == MSG_LED_TOGGLE) {
             bool state = !gpio_get(25);
@@ -47,12 +56,52 @@ static void task_sender(void* arg) {
     (void)arg;
     uint32_t seq = 0;
     for (;;) {
-        tk::sleep_ms(500);
+        tk::sleep_ms(600);
         P("Sender: posting MSG_LED_TOGGLE seq=%lu...\n", (unsigned long)seq);
         g_led_actor.post(MSG_LED_TOGGLE, seq++);
     }
 }
 
+// ---------------------------------------------------------------------------
+// Mutex demonstration: Two tasks contend for a shared counter protected by Mutex
+static tk::Mutex g_counter_mutex;
+static uint32_t g_shared_counter = 0;
+
+static void task_mutex_worker(void* arg) {
+    const char* tag = static_cast<const char*>(arg);
+    for (;;) {
+        {
+            tk::LockGuard<tk::Mutex> guard(g_counter_mutex);
+            g_shared_counter++;
+            P("[%s] holds mutex: counter=%lu\n", tag, (unsigned long)g_shared_counter);
+            volatile uint32_t n = 0;
+            while (n < 100000u) ++n;
+        }  // guard destructor automatically calls unlock()
+        tk::sleep_ms(350);
+    }
+}
+
+// ---------------------------------------------------------------------------
+// Semaphore demonstration: Signaler wakes up Waiter task
+static tk::BinarySemaphore g_demo_sem(0, 1);  // Initially 0 (blocked)
+
+static void task_sem_waiter(void* arg) {
+    (void)arg;
+    for (;;) {
+        g_demo_sem.acquire();  // Blocks until release() is called
+        P("SemWaiter: >>> WOKEN UP by semaphore! now=%llu ms\n", (unsigned long long)tk::now_ms());
+    }
+}
+
+static void task_sem_signaler(void* arg) {
+    (void)arg;
+    for (;;) {
+        tk::sleep_ms(1000);
+        P("SemSignaler: releasing semaphore...\n");
+        g_demo_sem.release();
+    }
+}
+
 // ---------------------------------------------------------------------------
 // Static task: FPU self-check. Runs the same deterministic float/double sequence
 // twice; any corruption of FPU state across context switches shows up as a
@@ -79,59 +128,25 @@ void task_fpu(void* arg) {
         } else {
             P("FPU ok    f: %8.6f   d: %.9f\n", (double)fa, da);
         }
-        tk::sleep_ms(300);
+        tk::sleep_ms(500);
     }
 }
 TK_TASK(task_fpu, tk::PRIO_NORMAL)
 
-// ---------------------------------------------------------------------------
-// Dynamic task (higher priority): busy-spins for a few ms to demonstrate that it
-// preempts the lower-priority tasks immediately.
-static void task_hi(void* arg) {
-    (void)arg;
-    for (;;) {
-        volatile uint32_t n = 0;
-        while (n < 500000u) ++n;
-        P("HI   prio=%u busy-spin done, now=%llu ms\n", (unsigned)tk::PRIO_HIGH, (unsigned long long)tk::now_ms());
-        tk::sleep_ms(700);
-    }
-}
-
-// ---------------------------------------------------------------------------
-// Dynamic task: prints a few times and then exits.
-static void task_dying(void* arg) {
-    (void)arg;
-    for (int i = 1; i <= 3; ++i) {
-        P("DYING %d (now=%llu ms)\n", i, (unsigned long long)tk::now_ms());
-        tk::sleep_ms(250);
-    }
-    P("DYING calling tk::exit()\n");
-    tk::exit(0);
-}
-
-// ---------------------------------------------------------------------------
-// Dynamic task: plain periodic printer at normal priority.
-static void task_print(void* arg) {
-    (void)arg;
-    uint32_t count = 0;
-    for (;;) {
-        P("A    #%lu now=%llu ms\n", (unsigned long)count++, (unsigned long long)tk::now_ms());
-        tk::sleep_ms(150);
-    }
-}
-
 int main() {
     stdio_init_all();
-    gpio_init(25);
-    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 (!g_led_actor.handle().is_valid() || !h_sender.is_valid() || !h_hi.is_valid() || !h_dy.is_valid() || !h_pr.is_valid()) {
+    auto h_mtx_a  = tk::create(task_mutex_worker, const_cast<char*>("Mtx-A"), tk::PRIO_NORMAL);
+    auto h_mtx_b  = tk::create(task_mutex_worker, const_cast<char*>("Mtx-B"), tk::PRIO_NORMAL);
+    auto h_waiter = tk::create(task_sem_waiter, nullptr, tk::PRIO_HIGH);
+    auto h_sig    = tk::create(task_sem_signaler, nullptr, tk::PRIO_NORMAL);
+
+    if (!g_led_actor.handle().is_valid() || !h_sender.is_valid() ||
+        !h_mtx_a.is_valid() || !h_mtx_b.is_valid() ||
+        !h_waiter.is_valid() || !h_sig.is_valid()) {
         printf("tinykernel: task creation FAILED\n");
         for (;;) {
         }

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

@@ -50,6 +50,9 @@ public:
     TaskHandle handle() const { return handle_; }
 
 protected:
+    // タスク起動時にイベントループ開始直前に呼ばれる初期化関数(タスクコンテキスト内)
+    virtual void on_start() {}
+
     // 派生クラスでオーバーライドしてメッセージを処理する
     virtual void on_message(const Message& msg) = 0;
 
@@ -59,6 +62,7 @@ private:
     }
 
     void event_loop() {
+        on_start();
         for (;;) {
             Message msg{};
             uint32_t save = detail::sched_lock();

+ 2 - 0
tinykernel/include/tinykernel/kernel.hpp

@@ -66,6 +66,8 @@ uint32_t sched_lock();
 void sched_unlock(uint32_t save);
 void block_current_task_locked();
 void wake_task_locked(TaskHandle handle);
+uint8_t current_task_slot();
+uint8_t find_highest_prio_waiter(uint16_t wait_mask);
 }  // namespace detail
 
 }  // namespace tk

+ 159 - 0
tinykernel/include/tinykernel/sync.hpp

@@ -0,0 +1,159 @@
+#pragma once
+
+#include "kernel.hpp"
+#include <cstdint>
+
+namespace tk {
+
+// ===========================================================================
+// Semaphore: Counting / Binary Semaphore
+// ===========================================================================
+class Semaphore {
+public:
+    explicit Semaphore(int32_t initial_count = 1, int32_t max_count = 1)
+        : count_(initial_count), max_count_(max_count) {}
+
+    // 資源を獲得するまでブロック(待機中はCPU消費ゼロ)
+    void acquire() {
+        uint32_t save = detail::sched_lock();
+        while (count_ <= 0) {
+            uint8_t slot = detail::current_task_slot();
+            wait_mask_ |= (1u << slot);
+            detail::block_current_task_locked();
+            detail::sched_unlock(save);
+            // 他タスクから release() されて再開したら再チェック
+            save = detail::sched_lock();
+        }
+        count_--;
+        detail::sched_unlock(save);
+    }
+
+    // ノンブロッキング獲得。獲得できれば true、できなければ false
+    bool try_acquire() {
+        uint32_t save = detail::sched_lock();
+        bool success = false;
+        if (count_ > 0) {
+            count_--;
+            success = true;
+        }
+        detail::sched_unlock(save);
+        return success;
+    }
+
+    // 資源を返却し、待機中タスクがあれば最優先度のタスクを1つ起床
+    void release() {
+        uint32_t save = detail::sched_lock();
+        if (count_ < max_count_) {
+            count_++;
+        }
+        if (wait_mask_ != 0) {
+            uint8_t slot = detail::find_highest_prio_waiter(wait_mask_);
+            if (slot != 0xFF) {
+                wait_mask_ &= ~(1u << slot);
+                detail::wake_task_locked(TaskHandle{slot});
+            }
+        }
+        detail::sched_unlock(save);
+    }
+
+    int32_t count() const {
+        uint32_t save = detail::sched_lock();
+        int32_t c = count_;
+        detail::sched_unlock(save);
+        return c;
+    }
+
+private:
+    int32_t count_;
+    int32_t max_count_;
+    uint16_t wait_mask_{0};
+};
+
+using BinarySemaphore = Semaphore;
+
+// ===========================================================================
+// Mutex: Mutual Exclusion Lock with Ownership and RAII Support
+// ===========================================================================
+class Mutex {
+public:
+    Mutex() = default;
+
+    // C++ BasicLockable: lock()
+    void lock() {
+        uint32_t save = detail::sched_lock();
+        uint8_t my_slot = detail::current_task_slot();
+        while (locked_) {
+            wait_mask_ |= (1u << my_slot);
+            detail::block_current_task_locked();
+            detail::sched_unlock(save);
+            save = detail::sched_lock();
+        }
+        locked_ = true;
+        owner_slot_ = my_slot;
+        detail::sched_unlock(save);
+    }
+
+    // C++ Lockable: try_lock()
+    bool try_lock() {
+        uint32_t save = detail::sched_lock();
+        uint8_t my_slot = detail::current_task_slot();
+        bool success = false;
+        if (!locked_) {
+            locked_ = true;
+            owner_slot_ = my_slot;
+            success = true;
+        }
+        detail::sched_unlock(save);
+        return success;
+    }
+
+    // C++ BasicLockable: unlock()
+    // 所有者以外のタスクが解除しようとした場合は安全に無視
+    void unlock() {
+        uint32_t save = detail::sched_lock();
+        uint8_t my_slot = detail::current_task_slot();
+        if (locked_ && owner_slot_ == my_slot) {
+            locked_ = false;
+            owner_slot_ = 0xFF;
+            if (wait_mask_ != 0) {
+                uint8_t slot = detail::find_highest_prio_waiter(wait_mask_);
+                if (slot != 0xFF) {
+                    wait_mask_ &= ~(1u << slot);
+                    detail::wake_task_locked(TaskHandle{slot});
+                }
+            }
+        }
+        detail::sched_unlock(save);
+    }
+
+    bool is_locked() const {
+        uint32_t save = detail::sched_lock();
+        bool lk = locked_;
+        detail::sched_unlock(save);
+        return lk;
+    }
+
+private:
+    bool locked_{false};
+    uint8_t owner_slot_{0xFF};
+    uint16_t wait_mask_{0};
+};
+
+// ===========================================================================
+// LockGuard: Lightweight RAII helper (std::lock_guard equivalent)
+// ===========================================================================
+template <typename Lockable>
+class LockGuard {
+public:
+    explicit LockGuard(Lockable& m) : m_(m) { m_.lock(); }
+    ~LockGuard() { m_.unlock(); }
+
+    LockGuard(const LockGuard&) = delete;
+    LockGuard& operator=(const LockGuard&) = delete;
+
+private:
+    Lockable& m_;
+};
+
+}  // namespace tk
+

+ 18 - 0
tinykernel/src/scheduler.cpp

@@ -332,3 +332,21 @@ void tk::detail::wake_task_locked(TaskHandle handle) {
         tk_port_trigger_pendsv();
     }
 }
+
+uint8_t tk::detail::current_task_slot() {
+    return g_cur[get_core_num()];
+}
+
+uint8_t tk::detail::find_highest_prio_waiter(uint16_t wait_mask) {
+    uint8_t best_slot = 0xFF;
+    uint32_t best_prio = 0xFFFFFFFFu;
+    for (uint32_t i = 0; i < TK_MAX_TASKS; ++i) {
+        if (wait_mask & (1u << i)) {
+            if (g_slots[i].prio < best_prio) {
+                best_prio = g_slots[i].prio;
+                best_slot = (uint8_t)i;
+            }
+        }
+    }
+    return best_slot;
+}