summaryrefslogtreecommitdiff
path: root/na/kernel/waitqueue.cc
blob: 9ee3f463492109b9cae330f1d415ef190eb169a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */

/*
 * WaitQueue — blocking synchronization implementation
 *
 * sleep():    current task → Sleeping, link into queue, schedule()
 * wake_one(): dequeue head task → Runnable, sched_enqueue()
 *
 * The internal spinlock protects the wait list only.
 * schedule() is called AFTER releasing the spinlock to avoid
 * holding a lock across a context switch.
 */

#include <lume/waitqueue.h>
#include <lume/task.h>
#include <lume/sched.h>
#include <lume/kprintf.h>
#include <arch/cpu.h>

void WaitQueue::sleep(Spinlock* external_lock)
{
    TaskControlBlock* curr = current_task();
    if (!curr) {
        if (external_lock) external_lock->release();
        return;
    }

    {
        LockGuard guard(lock_);

        /* Mark task as sleeping */
        curr->state = TaskState::Sleeping;

        /* Remove from RunQueue (sched_dequeue acquires its own lock) */
        /* We do NOT call sched_dequeue here because schedule() handles
         * the put_prev_task logic when state != Running. */

        /* Link into wait queue tail */
        list_node* node = &curr->run_link;
        node->prev = head_.prev;
        node->next = &head_;
        head_.prev->next = node;
        head_.prev = node;
    }

    if (external_lock) {
        external_lock->release();
    }

    /* Context switch away — will return here when woken */
    schedule();
}

bool WaitQueue::wake_one()
{
    LockGuard guard(lock_);

    if (head_.is_empty())
        return false;

    /* Dequeue the first (oldest) waiter */
    list_node* node = head_.next;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    node->init();

    TaskControlBlock* task =
        list_entry<TaskControlBlock, &TaskControlBlock::run_link>(node);

    task->state = TaskState::Runnable;
    sched_enqueue(task);

    return true;
}

int WaitQueue::wake_all()
{
    LockGuard guard(lock_);

    int count = 0;
    while (!head_.is_empty()) {
        list_node* node = head_.next;
        node->prev->next = node->next;
        node->next->prev = node->prev;
        node->init();

        TaskControlBlock* task =
            list_entry<TaskControlBlock, &TaskControlBlock::run_link>(node);

        task->state = TaskState::Runnable;
        sched_enqueue(task);
        count++;
    }

    return count;
}