Kaynağa Gözat

Add: preempt_disable/preempt_enable/PreemptGuardを追加

Satoshi Yoneda 1 ay önce
ebeveyn
işleme
2026d00a75

+ 26 - 7
main.cpp

@@ -131,22 +131,41 @@ void task_fpu(void* arg) {
         tk::sleep_ms(500);
     }
 }
-TK_TASK(task_fpu, tk::PRIO_NORMAL)
+// ---------------------------------------------------------------------------
+// Preemption Guard demonstration: Simulates an atomic peripheral transaction (e.g. I2C)
+// During PreemptGuard, no task switch occurs even if time slices expire or interrupts fire.
+static void task_preempt_demo(void* arg) {
+    (void)arg;
+    for (;;) {
+        tk::sleep_ms(2000);
+        {
+            tk::PreemptGuard guard;
+            uint64_t t0 = tk::now_ms();
+            P("PreemptDemo: >>> START atomic section (preemption disabled), t0=%llu ms\n", (unsigned long long)t0);
+            // Busy wait 60ms (exceeds the 20ms time slice, but preemption is blocked!)
+            // Meanwhile SysTick interrupt keeps running, so now_ms() keeps updating!
+            while (tk::now_ms() - t0 < 60) {
+            }
+            P("PreemptDemo: <<< END atomic section, now=%llu ms (SysTick kept advancing!)\n", (unsigned long long)tk::now_ms());
+        }  // guard destructor calls preempt_enable() -> deferred task switches happen now!
+    }
+}
 
 int main() {
     stdio_init_all();
 
     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_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);
+    auto h_sender  = tk::create(task_sender, nullptr, tk::PRIO_NORMAL);
+    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);
+    auto h_preempt = tk::create(task_preempt_demo, 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()) {
+        !h_waiter.is_valid() || !h_sig.is_valid() || !h_preempt.is_valid()) {
         printf("tinykernel: task creation FAILED\n");
         for (;;) {
         }

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

@@ -60,6 +60,21 @@ uint64_t now_ms();
 bool lock();
 void unlock(bool was_enabled);
 
+// Preemption disable/enable for the current core without disabling interrupts.
+// Useful for peripheral transactions (I2C, SPI) that should not be interrupted by other tasks.
+// Nesting is supported.
+void preempt_disable();
+void preempt_enable();
+
+class PreemptGuard {
+public:
+    PreemptGuard() { preempt_disable(); }
+    ~PreemptGuard() { preempt_enable(); }
+
+    PreemptGuard(const PreemptGuard&) = delete;
+    PreemptGuard& operator=(const PreemptGuard&) = delete;
+};
+
 namespace detail {
 void static_register(const char* name, TaskFn fn, uint32_t prio);
 uint32_t sched_lock();

+ 25 - 0
tinykernel/src/scheduler.cpp

@@ -38,6 +38,7 @@ alignas(16) static TaskSlot g_slots[TK_MAX_TASKS];  // slot 0 = idle0, slot 1 =
 alignas(8) static char g_stacks[TK_MAX_TASKS][TK_DEFAULT_STACK_SIZE];
 
 static uint8_t g_cur[TK_NUM_CORES] = {0, 1};
+static volatile uint32_t g_preempt_disabled[TK_NUM_CORES] = {0, 0};
 static int64_t g_tick = 0;
 static bool g_started = false;
 static uint8_t g_rr_last[16] = {0};
@@ -147,6 +148,11 @@ extern "C" uintptr_t tk_switch_context(uintptr_t cur_sp) {
     uint32_t save = sched_lock();
 
     TaskSlot& cur = g_slots[g_cur[core]];
+    if (g_preempt_disabled[core] > 0 && cur.state == State::Running) {
+        sched_unlock(save);
+        return cur_sp;
+    }
+
     cur.sp = cur_sp;
     memcpy(&cur.fpu, &tk_fpu_scratch[core], sizeof(cur.fpu));
     if (cur.state == State::Running) {
@@ -350,3 +356,22 @@ uint8_t tk::detail::find_highest_prio_waiter(uint16_t wait_mask) {
     }
     return best_slot;
 }
+
+void tk::preempt_disable() {
+    uint32_t core = get_core_num();
+    uint32_t save = sched_lock();
+    ++g_preempt_disabled[core];
+    sched_unlock(save);
+}
+
+void tk::preempt_enable() {
+    uint32_t core = get_core_num();
+    uint32_t save = sched_lock();
+    if (g_preempt_disabled[core] > 0) {
+        --g_preempt_disabled[core];
+        if (g_preempt_disabled[core] == 0) {
+            tk_port_trigger_pendsv();
+        }
+    }
+    sched_unlock(save);
+}