Quellcode durchsuchen

refactor: rewrite thread and readyqueue

remove class readyqueue and use three functions as its alternative

remove evtqueue and input message support
TODO: add vga console support back

divide process.hpp into several files

remove old cond_var and use wait_list as an alternative
    - now we need an exclusive mutex to manage desired resource

global_find.sh: search .cc files as well
greatbridf vor 11 Monaten
Ursprung
Commit
ad3ff833e5

+ 7 - 4
CMakeLists.txt

@@ -37,6 +37,7 @@ set(BOOTLOADER_SOURCES src/boot.s
 
 set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kinit.cpp
+                        src/kernel/async/waitlist.cc
                         src/kernel/allocator.cc
                         src/kernel/interrupt.cpp
                         src/kernel/process.cpp
@@ -51,7 +52,8 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kernel/hw/pci.cc
                         src/kernel/hw/serial.cpp
                         src/kernel/hw/timer.c
-                        src/kernel/event/event.cpp
+                        src/kernel/task/thread.cc
+                        src/kernel/task/readyqueue.cc
                         src/kernel/user/thread_local.cc
                         src/kernel/vfs/tmpfs.cc
                         src/kernel/signal.cpp
@@ -60,8 +62,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/asm/port_io.h
                         include/asm/sys.h
                         include/fs/fat.hpp
-                        include/kernel/event/event.h
-                        include/kernel/event/evtqueue.hpp
+                        include/kernel/async/waitlist.hpp
                         include/kernel/tty.hpp
                         include/kernel/interrupt.h
                         include/kernel/irq.hpp
@@ -78,13 +79,15 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/kernel/vfs/vfs.hpp
                         include/kernel/vga.hpp
                         include/kernel/signal.hpp
+                        include/kernel/task/forward.hpp
+                        include/kernel/task/thread.hpp
+                        include/kernel/task/readyqueue.hpp
                         include/kernel/hw/keyboard.h
                         include/kernel/hw/pci.hpp
                         include/kernel/hw/port.hpp
                         include/kernel/hw/serial.h
                         include/kernel/hw/timer.h
                         include/kernel/input/keycodes.h
-                        include/kernel/input/input_event.h
                         include/kernel/user/thread_local.hpp
                         include/types/bitmap.hpp
                         include/types/buffer.hpp

+ 1 - 1
global_find.sh

@@ -9,4 +9,4 @@ do_find()
     done
 }
 
-do_find "$1" "c h cpp hpp s"
+do_find "$1" "c h cpp hpp s cc"

+ 30 - 0
include/kernel/async/waitlist.hpp

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <set>
+
+#include <types/lock.hpp>
+
+#include <kernel/task/forward.hpp>
+
+namespace kernel::async {
+
+class wait_list {
+private:
+    types::mutex m_mtx;
+    std::set<task::thread*> m_subscribers;
+
+    wait_list(const wait_list&) = delete;
+
+public:
+    explicit wait_list() = default;
+
+    // @return whether the wait is interrupted
+    bool wait(types::mutex& lck);
+
+    void subscribe();
+
+    void notify_one();
+    void notify_all();
+};
+
+} // namespace kernel

+ 0 - 16
include/kernel/event/event.h

@@ -1,16 +0,0 @@
-#pragma once
-
-#include <kernel/input/input_event.h>
-#include <types/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void commit_input_event(struct input_event* evt);
-
-void dispatch_event(void);
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 38
include/kernel/event/evtqueue.hpp

@@ -1,38 +0,0 @@
-#pragma once
-
-#include <list>
-
-#include <types/cplusplus.hpp>
-#include <types/lock.hpp>
-
-namespace kernel {
-
-namespace tasks {
-
-// declaration in kernel/process.hpp
-struct thread;
-
-} // namespace tasks
-
-class cond_var : public types::non_copyable {
-private:
-    using list_type = std::list<tasks::thread*>;
-
-    types::mutex m_mtx;
-    list_type m_subscribers;
-
-public:
-    cond_var(void) = default;
-
-    constexpr types::mutex& mtx(void)
-    {
-        return m_mtx;
-    }
-
-    /// @param lock should have already been locked
-    bool wait(types::mutex& lock);
-    void notify(void);
-    void notify_all(void);
-};
-
-} // namespace kernel

+ 14 - 122
include/kernel/process.hpp

@@ -10,7 +10,13 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <kernel/event/evtqueue.hpp>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <kernel/task/thread.hpp>
+#include <kernel/task/current.hpp>
+
+#include <kernel/async/waitlist.hpp>
 #include <kernel/interrupt.h>
 #include <kernel/mm.hpp>
 #include <kernel/mem.h>
@@ -19,8 +25,6 @@
 #include <kernel/task.h>
 #include <kernel/tty.hpp>
 #include <kernel/vfs.hpp>
-#include <stdint.h>
-#include <sys/types.h>
 #include <types/allocator.hpp>
 #include <types/cplusplus.hpp>
 #include <types/hash_map.hpp>
@@ -28,22 +32,14 @@
 #include <types/status.h>
 #include <types/string.hpp>
 #include <types/types.h>
+#include <types/lock.hpp>
 
 class process;
 
-namespace kernel::tasks {
-
-struct thread;
-
-} // namespace kernel::tasks
-
 class proclist;
-class readyqueue;
 
 inline process* volatile current_process;
-inline kernel::tasks::thread* volatile current_thread;
 inline proclist* procs;
-inline readyqueue* readythds;
 
 inline tss32_t tss;
 
@@ -57,69 +53,6 @@ struct thread_attr {
     uint32_t ready : 1;
 };
 
-namespace kernel::tasks {
-
-using tid_t = uint32_t;
-
-struct thread {
-private:
-    struct kernel_stack {
-        std::byte* stack_base;
-        uint32_t* esp;
-
-        kernel_stack();
-        kernel_stack(const kernel_stack& other);
-        kernel_stack(kernel_stack&& other);
-        ~kernel_stack();
-    };
-
-public:
-    kernel_stack kstack;
-    pid_t owner;
-    thread_attr attr;
-    signal_list signals;
-
-    int* __user set_child_tid {};
-    int* __user clear_child_tid {};
-
-    types::string<> name {};
-
-    segment_descriptor tls_desc {};
-
-    explicit inline thread(types::string<> name, pid_t owner)
-        : owner { owner }
-        , attr { .system = 1, .ready = 1, }
-        , name { name }
-    {
-    }
-
-    inline thread(const thread& val, pid_t owner)
-        : owner { owner }, attr { val.attr }, name { val.name }
-    {
-    }
-
-    int set_thread_area(user::user_desc* ptr);
-    int load_thread_area() const;
-
-    void sleep();
-    void wakeup();
-    constexpr bool is_ready() const
-    { return attr.ready; }
-
-    void send_signal(kernel::signal_list::signo_type signal);
-
-    thread(thread&& val) = default;
-
-    inline tid_t tid() const { return (tid_t)kstack.stack_base; }
-
-    inline bool operator==(const thread& rhs) const
-    { return tid() == rhs.tid(); }
-    inline bool operator<(const thread& rhs) const
-    { return tid() < rhs.tid(); }
-};
-
-}
-
 class filearr {
 private:
     // TODO: change this
@@ -245,9 +178,12 @@ public:
 
 public:
     kernel::memory::mm_list mms {};
-    std::set<kernel::tasks::thread> thds;
-    kernel::cond_var cv_wait;
-    std::list<wait_obj> waitlist;
+    std::set<kernel::task::thread> thds;
+    kernel::async::wait_list waitlist;
+
+    types::mutex mtx_waitprocs;
+    std::list<wait_obj> waitprocs;
+
     process_attr attr {};
     filearr files;
     types::path pwd;
@@ -360,50 +296,6 @@ public:
     void kill(pid_t pid, int exit_code);
 };
 
-// TODO: lock and unlock
-class readyqueue final {
-public:
-    using thread = kernel::tasks::thread;
-    using list_type = std::list<thread*>;
-
-private:
-    list_type m_thds;
-
-private:
-    readyqueue(const readyqueue&) = delete;
-    readyqueue(readyqueue&&) = delete;
-    readyqueue& operator=(const readyqueue&) = delete;
-    readyqueue& operator=(readyqueue&&) = delete;
-
-    ~readyqueue() = delete;
-
-public:
-    constexpr explicit readyqueue(void) = default;
-
-    constexpr void push(thread* thd)
-    { m_thds.push_back(thd); }
-
-    constexpr thread* pop(void)
-    {
-        m_thds.remove_if([](thread* item) {
-            return !item->is_ready();
-        });
-        auto* retval = m_thds.front();
-        m_thds.pop_front();
-        return retval;
-    }
-
-    constexpr thread* query(void)
-    {
-        auto* thd = this->pop();
-        this->push(thd);
-        return thd;
-    }
-
-    constexpr void remove_all(thread* thd)
-    { m_thds.remove(thd); }
-};
-
 void NORETURN init_scheduler(void);
 /// @return true if returned normally, false if being interrupted
 bool schedule(void);

+ 5 - 0
include/kernel/task/current.hpp

@@ -0,0 +1,5 @@
+#pragma once
+
+#include <kernel/task/thread.hpp>
+
+inline kernel::task::thread* volatile current_thread;

+ 7 - 0
include/kernel/task/forward.hpp

@@ -0,0 +1,7 @@
+#pragma once
+
+namespace kernel::task {
+
+class thread;
+
+} // namespace kernel::task

+ 16 - 0
include/kernel/task/readyqueue.hpp

@@ -0,0 +1,16 @@
+#pragma once
+
+#include <list>
+
+#include <types/lock.hpp>
+
+#include <kernel/task/thread.hpp>
+
+namespace kernel::task::dispatcher {
+
+void enqueue(thread* thd);
+void dequeue(thread* thd);
+
+thread* next();
+
+} // namespace kernel::task

+ 70 - 0
include/kernel/task/thread.hpp

@@ -0,0 +1,70 @@
+#pragma once
+
+#include <cstddef>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <types/string.hpp>
+#include <types/types.h>
+
+#include <kernel/signal.hpp>
+#include <kernel/user/thread_local.hpp>
+
+namespace kernel::task {
+
+using tid_t = uint32_t;
+
+struct thread {
+public:
+    using thd_attr_t = uint32_t;
+    static constexpr thd_attr_t SYSTEM  = 0x01;
+    static constexpr thd_attr_t READY   = 0x02;
+    static constexpr thd_attr_t STOPPED = 0x04;
+    static constexpr thd_attr_t ZOMBIE  = 0x08;
+    static constexpr thd_attr_t ISLEEP  = 0x10;
+    static constexpr thd_attr_t USLEEP  = 0x20;
+
+private:
+    struct kernel_stack {
+        std::byte* stack_base;
+        uint32_t* esp;
+
+        kernel_stack();
+        kernel_stack(const kernel_stack& other);
+        kernel_stack(kernel_stack&& other);
+        ~kernel_stack();
+    };
+
+public:
+    kernel_stack kstack;
+    pid_t owner;
+    thd_attr_t attr;
+    signal_list signals;
+
+    int* __user set_child_tid {};
+    int* __user clear_child_tid {};
+
+    types::string<> name {};
+
+    segment_descriptor tls_desc {};
+
+    explicit thread(types::string<> name, pid_t owner);
+    thread(const thread& val, pid_t owner);
+
+    int set_thread_area(user::user_desc* ptr);
+    int load_thread_area() const;
+
+    void set_attr(thd_attr_t new_attr);
+
+    void send_signal(signal_list::signo_type signal);
+
+    thread(thread&& val) = default;
+
+    tid_t tid() const;
+
+    bool operator<(const thread& rhs) const;
+    bool operator==(const thread& rhs) const;
+};
+
+} // namespace kernel::task

+ 5 - 2
include/kernel/tty.hpp

@@ -4,10 +4,12 @@
 #include <sys/types.h>
 #include <termios.h>
 
-#include <kernel/event/evtqueue.hpp>
 #include <types/allocator.hpp>
 #include <types/buffer.hpp>
 #include <types/cplusplus.hpp>
+#include <types/lock.hpp>
+
+#include <kernel/async/waitlist.hpp>
 
 class tty : public types::non_copyable {
 public:
@@ -50,8 +52,9 @@ public:
     termios termio;
 
 protected:
+    types::mutex mtx_buf;
     types::buffer buf;
-    kernel::cond_var m_cv;
+    kernel::async::wait_list waitlist;
 
     pid_t fg_pgroup;
 };

+ 4 - 2
include/kernel/vfs/file.hpp

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <kernel/async/waitlist.hpp>
 #include <kernel/vfs/dentry.hpp>
 
 #include <errno.h>
@@ -8,7 +9,7 @@
 
 #include <types/types.h>
 #include <types/buffer.hpp>
-#include <kernel/event/evtqueue.hpp>
+#include <types/lock.hpp>
 
 namespace fs {
 
@@ -20,7 +21,8 @@ private:
 
 private:
     types::buffer buf;
-    kernel::cond_var m_cv;
+    kernel::async::wait_list waitlist;
+    types::mutex mtx;
     uint32_t flags;
 
 public:

+ 63 - 0
src/kernel/async/waitlist.cc

@@ -0,0 +1,63 @@
+#include <kernel/async/waitlist.hpp>
+
+#include <assert.h>
+
+#include <types/lock.hpp>
+
+#include <kernel/process.hpp>
+#include <kernel/task/thread.hpp>
+
+using namespace kernel::async;
+
+bool wait_list::wait(types::mutex& lock)
+{
+    this->subscribe();
+
+    auto* curthd = current_thread;
+    curthd->set_attr(kernel::task::thread::ISLEEP);
+
+    lock.unlock();
+    bool has_signals = schedule();
+    lock.lock();
+
+    m_subscribers.erase(curthd);
+    return !has_signals;
+}
+
+void wait_list::subscribe()
+{
+    types::lock_guard lck(m_mtx);
+
+    auto* thd = current_thread;
+
+    bool inserted;
+    std::tie(std::ignore, inserted) = m_subscribers.insert(thd);
+
+    assert(inserted);
+}
+
+void wait_list::notify_one()
+{
+    types::lock_guard lck(m_mtx);
+
+    if (m_subscribers.empty())
+        return;
+
+    auto iter = m_subscribers.begin();
+    (*iter)->set_attr(kernel::task::thread::READY);
+
+    m_subscribers.erase(iter);
+}
+
+void wait_list::notify_all()
+{
+    types::lock_guard lck(m_mtx);
+
+    if (m_subscribers.empty())
+        return;
+
+    for (auto thd : m_subscribers)
+        thd->set_attr(kernel::task::thread::READY);
+
+    m_subscribers.clear();
+}

+ 0 - 85
src/kernel/event/event.cpp

@@ -1,85 +0,0 @@
-#include <asm/port_io.h>
-#include <assert.h>
-#include <kernel/event/event.h>
-#include <kernel/event/evtqueue.hpp>
-#include <kernel/input/input_event.h>
-#include <kernel/log.hpp>
-#include <kernel/process.hpp>
-#include <stdio.h>
-#include <types/allocator.hpp>
-#include <types/cplusplus.hpp>
-#include <types/lock.hpp>
-
-static std::list<::input_event>* _input_event_queue;
-
-namespace event {
-std::list<::input_event>& input_event_queue(void)
-{
-    if (!_input_event_queue) {
-        _input_event_queue = new std::list<input_event>;
-    }
-    return *_input_event_queue;
-}
-} // namespace event
-
-void commit_input_event(struct input_event* evt)
-{
-    event::input_event_queue().push_back(*evt);
-}
-
-void dispatch_event(void)
-{
-    char buf[1024];
-    auto& input_event_queue = event::input_event_queue();
-
-    // char* ptr = (char*)0x8000000;
-    // *ptr = 0xff;
-
-    while (!input_event_queue.empty()) {
-        for (auto iter = input_event_queue.begin(); iter != input_event_queue.end(); ++iter) {
-            const auto& item = *iter;
-            snprintf(buf, 1024, "\rinput event: type%x, data%x, code%x\r", item.type, item.data, item.code);
-            kmsg(buf);
-            input_event_queue.erase(iter);
-        }
-    }
-}
-
-bool kernel::cond_var::wait(types::mutex& lock)
-{
-    kernel::tasks::thread* thd = current_thread;
-
-    current_thread->sleep();
-    m_subscribers.push_back(thd);
-
-    lock.unlock();
-    bool ret = schedule();
-    lock.lock();
-
-    m_subscribers.remove(thd);
-    return ret;
-}
-
-void kernel::cond_var::notify(void)
-{
-    types::lock_guard lck(m_mtx);
-
-    auto iter = m_subscribers.begin();
-    if (iter == m_subscribers.end())
-        return;
-
-    auto* thd = *iter;
-    thd->wakeup();
-
-    m_subscribers.erase(iter);
-}
-
-void kernel::cond_var::notify_all(void)
-{
-    types::lock_guard lck(m_mtx);
-
-    for (auto& thd : m_subscribers)
-        thd->wakeup();
-
-    m_subscribers.clear();
-}

+ 3 - 2
src/kernel/hw/keyboard.cpp

@@ -1,5 +1,4 @@
 #include <asm/port_io.h>
-#include <kernel/event/event.h>
 #include <kernel/hw/keyboard.h>
 #include <kernel/input/input_event.h>
 
@@ -26,5 +25,7 @@ handle_keyboard_interrupt(void)
 
     evt.data = keycode;
 
-    commit_input_event(&evt);
+    // TODO: fix it
+    // commit_input_event(&evt);
+    (void)evt;
 }

+ 27 - 125
src/kernel/process.cpp

@@ -5,6 +5,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <bits/alltypes.h>
+#include <sys/wait.h>
 
 #include <asm/port_io.h>
 #include <asm/sys.h>
@@ -19,6 +20,8 @@
 #include <kernel/signal.hpp>
 #include <kernel/vfs.hpp>
 #include <kernel/user/thread_local.hpp>
+#include <kernel/task/thread.hpp>
+#include <kernel/task/readyqueue.hpp>
 
 #include <types/allocator.hpp>
 #include <types/bitmap.hpp>
@@ -220,7 +223,6 @@ process::process(pid_t pid, pid_t ppid)
     assert(inserted);
 }
 
-using kernel::tasks::thread;
 using signo_type = kernel::signal_list::signo_type;
 
 void process::send_signal(signo_type signal)
@@ -229,108 +231,6 @@ void process::send_signal(signo_type signal)
         thd.send_signal(signal);
 }
 
-static std::priority_queue<std::byte*> s_kstacks;
-thread::kernel_stack::kernel_stack()
-{
-    static int allocated;
-    static types::mutex mtx;
-    types::lock_guard lck(mtx);
-
-    if (!s_kstacks.empty()) {
-        stack_base = s_kstacks.top();
-        esp = (uint32_t*)stack_base;
-        s_kstacks.pop();
-        return;
-    }
-
-    // kernel stack pt is at page#0x00005
-    kernel::paccess pa(0x00005);
-    auto pt = (pt_t)pa.ptr();
-    assert(pt);
-
-    int cnt = THREAD_KERNEL_STACK_SIZE / PAGE_SIZE;
-    pte_t* pte = *pt + allocated * cnt;
-
-    for (int i = 0; i < cnt; ++i) {
-        pte[i].v = 0x3;
-        pte[i].in.page = __alloc_raw_page();
-    }
-
-    stack_base = (std::byte*)(0xffc00000 + THREAD_KERNEL_STACK_SIZE * (allocated + 1));
-    esp = (uint32_t*)stack_base;
-
-    ++allocated;
-}
-
-thread::kernel_stack::kernel_stack(const kernel_stack& other)
-    : kernel_stack()
-{
-    auto offset = vptrdiff(other.stack_base, other.esp);
-    esp = (uint32_t*)(stack_base - offset);
-    memcpy(esp, other.esp, offset);
-}
-
-thread::kernel_stack::kernel_stack(kernel_stack&& other)
-    : stack_base(std::exchange(other.stack_base, nullptr))
-    , esp(std::exchange(other.esp, nullptr)) { }
-
-thread::kernel_stack::~kernel_stack()
-{
-    s_kstacks.push(stack_base);
-}
-
-void thread::sleep()
-{
-    attr.ready = 0;
-    readythds->remove_all(this);
-}
-
-void thread::wakeup()
-{
-    attr.ready = 1;
-    readythds->push(this);
-}
-
-void thread::send_signal(signo_type signal)
-{
-    if (signals.raise(signal))
-        this->wakeup();
-}
-
-int thread::set_thread_area(kernel::user::user_desc* ptr)
-{
-    if (ptr->read_exec_only && ptr->seg_not_present) {
-        void* dst = (void*)ptr->base_addr;
-        std::size_t len = ptr->limit;
-        if (len > 0 && dst)
-            memset(dst, 0x00, len);
-        return 0;
-    }
-
-    if (ptr->entry_number == -1U)
-        ptr->entry_number = 6;
-    else
-        return -1;
-
-    tls_desc.limit_low = ptr->limit & 0xFFFF;
-    tls_desc.base_low = ptr->base_addr & 0xFFFF;
-    tls_desc.base_mid = (ptr->base_addr >> 16) & 0xFF;
-    tls_desc.access = SD_TYPE_DATA_USER;
-    tls_desc.limit_high = (ptr->limit >> 16) & 0xF;
-    tls_desc.flags = (ptr->limit_in_pages << 3) | (ptr->seg_32bit << 2);
-    tls_desc.base_high = (ptr->base_addr >> 24) & 0xFF;
-
-    return 0;
-}
-
-int thread::load_thread_area() const
-{
-    if (tls_desc.flags == 0)
-        return -1;
-    kernel::user::load_thread_area(tls_desc);
-    return 0;
-}
-
 void kernel_threadd_main(void)
 {
     kmsg("kernel thread daemon started\n");
@@ -380,7 +280,8 @@ proclist::proclist()
 
     current_process = &init;
     current_thread = &thd;
-    readythds->push(current_thread);
+
+    kernel::task::dispatcher::enqueue(current_thread);
 
     tss.ss0 = KERNEL_DATA_SEGMENT;
     tss.esp0 = (uint32_t)current_thread->kstack.esp;
@@ -414,7 +315,7 @@ proclist::proclist()
         // original esp
         push_stack(esp, old_esp);
 
-        readythds->push(&thd);
+        kernel::task::dispatcher::enqueue(&thd);
     }
 }
 
@@ -432,7 +333,7 @@ void proclist::kill(pid_t pid, int exit_code)
 
     // put all threads into sleep
     for (auto& thd : proc.thds)
-        thd.sleep();
+        thd.set_attr(kernel::task::thread::ZOMBIE);
 
     // write back mmap'ped files and close them
     proc.files.close_all();
@@ -456,31 +357,33 @@ void proclist::kill(pid_t pid, int exit_code)
     auto& init = this->find(1);
 
     bool flag = false;
-    {
-        auto& mtx = init.cv_wait.mtx();
-        types::lock_guard lck(mtx);
+    if (1) {
+        types::lock_guard lck(init.mtx_waitprocs);
 
-        {
-            auto& mtx = proc.cv_wait.mtx();
-            types::lock_guard lck(mtx);
+        if (1) {
+            types::lock_guard lck(proc.mtx_waitprocs);
 
-            for (const auto& item : proc.waitlist) {
-                init.waitlist.push_back(item);
+            for (const auto& item : proc.waitprocs) {
+                if (WIFSTOPPED(item.code) || WIFCONTINUED(item.code))
+                    continue;
+
+                init.waitprocs.push_back(item);
                 flag = true;
             }
 
-            proc.waitlist.clear();
+            proc.waitprocs.clear();
         }
     }
+
     if (flag)
-        init.cv_wait.notify();
+        init.waitlist.notify_all();
 
-    {
-        auto& mtx = parent.cv_wait.mtx();
-        types::lock_guard lck(mtx);
-        parent.waitlist.push_back({ pid, exit_code });
+    if (1) {
+        types::lock_guard lck(parent.mtx_waitprocs);
+        parent.waitprocs.push_back({ pid, exit_code });
     }
-    parent.cv_wait.notify();
+
+    parent.waitlist.notify_all();
 }
 
 static void release_kinit()
@@ -551,7 +454,7 @@ void NORETURN _kernel_init(void)
     }
 
     current_process->attr.system = 0;
-    current_thread->attr.system = 0;
+    current_thread->attr |= kernel::task::thread::SYSTEM;
 
     const char* argv[] = { "/mnt/busybox", "sh", "/mnt/initsh" };
     const char* envp[] = { "LANG=C", "HOME=/root", "PATH=/mnt", "PWD=/", nullptr };
@@ -601,7 +504,6 @@ void k_new_thread(void (*func)(void*), void* data)
 SECTION(".text.kinit")
 void NORETURN init_scheduler(void)
 {
-    readythds = new readyqueue;
     procs = new proclist;
 
     asm volatile(
@@ -636,9 +538,9 @@ void NORETURN init_scheduler(void)
 extern "C" void asm_ctx_switch(uint32_t** curr_esp, uint32_t** next_esp);
 bool schedule()
 {
-    auto next_thd = readythds->query();
+    auto* next_thd = kernel::task::dispatcher::next();
     process* proc = nullptr;
-    kernel::tasks::thread* curr_thd = nullptr;
+    kernel::task::thread* curr_thd = nullptr;
 
     if (current_thread == next_thd)
         goto _end;

+ 12 - 1
src/kernel/signal.cpp

@@ -1,3 +1,4 @@
+#include <kernel/task/thread.hpp>
 #include <kernel/process.hpp>
 #include <kernel/signal.hpp>
 #include <kernel/interrupt.h>
@@ -23,12 +24,22 @@ using signo_type = signal_list::signo_type;
 
 static void continue_process(int signal)
 {
+    auto& parent = procs->find(current_process->ppid);
+
+    // signal parent we're running
+    parent.waitprocs.push_back({ current_process->pid, 0xffff });
+
     current_thread->signals.after_signal(signal);
 }
 
 static void stop_process(int signal)
 {
-    current_thread->sleep();
+    auto& parent = procs->find(current_process->ppid);
+
+    current_thread->set_attr(kernel::task::thread::STOPPED);
+
+    // signal parent we're stopped
+    parent.waitprocs.push_back({ current_process->pid, 0x7f });
 
     while (true) {
         if (schedule())

+ 31 - 20
src/kernel/syscall.cpp

@@ -16,8 +16,11 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/utsname.h>
+#include <sys/wait.h>
 
 #include <kernel/user/thread_local.hpp>
+#include <kernel/task/readyqueue.hpp>
+#include <kernel/task/thread.hpp>
 #include <kernel/interrupt.h>
 #include <kernel/log.hpp>
 #include <kernel/mem.h>
@@ -68,7 +71,7 @@ int _syscall_fork(interrupt_stack* data)
     assert(inserted);
     auto* newthd = &*iter_newthd;
 
-    readythds->push(newthd);
+    kernel::task::dispatcher::enqueue(newthd);
 
     uint32_t newthd_oldesp = (uint32_t)newthd->kstack.esp;
     auto esp = &newthd->kstack.esp;
@@ -139,7 +142,7 @@ int _syscall_read(interrupt_stack* data)
 // TODO: sleep seconds
 int _syscall_sleep(interrupt_stack*)
 {
-    current_thread->sleep();
+    current_thread->set_attr(kernel::task::thread::USLEEP);
 
     schedule();
     return 0;
@@ -227,39 +230,47 @@ int _syscall_waitpid(interrupt_stack* data)
     if (pid_to_wait != -1)
         return -EINVAL;
 
-    auto& cv = current_process->cv_wait;
-    auto& mtx = cv.mtx();
-    types::lock_guard lck(mtx);
+    auto& cv = current_process->waitlist;
+    types::lock_guard lck(current_process->mtx_waitprocs);
 
-    auto& waitlist = current_process->waitlist;
+    auto& waitlist = current_process->waitprocs;
 
     // TODO: check if it is waiting for stopped process
-    (void)options;
+    if (options & ~(WNOHANG | WUNTRACED)) {
+        NOT_IMPLEMENTED;
+        return -EINVAL;
+    }
 
     while (waitlist.empty()) {
         if (current_process->children.empty())
             return -ECHILD;
 
-        cv.wait(mtx);
+        if (options & WNOHANG)
+            return 0;
 
-        // TODO: check WNOHANG
-        // if (!cv.wait(mtx))
-        //     return -EINTR;
+        bool interrupted = cv.wait(current_process->mtx_waitprocs);
+        if (interrupted)
+            return -EINTR;
     }
 
-    auto iter = waitlist.begin();
-    assert(iter != waitlist.end());
+    for (auto iter = waitlist.begin(); iter != waitlist.end(); ++iter) {
+        if (WIFSTOPPED(iter->code) && !(options & WUNTRACED))
+            continue;
 
-    auto& obj = *iter;
-    pid_t pid = obj.pid;
+        pid_t pid = iter->pid;
 
-    // TODO: copy_to_user check privilege
-    *arg1 = obj.code;
+        // TODO: copy_to_user check privilege
+        *arg1 = iter->code;
 
-    procs->remove(pid);
-    waitlist.erase(iter);
+        procs->remove(pid);
+        waitlist.erase(iter);
+
+        return pid;
+    }
 
-    return pid;
+    // we should never reach here
+    assert(false);
+    return -EINVAL;
 }
 
 int _syscall_wait4(interrupt_stack* data)

+ 38 - 0
src/kernel/task/readyqueue.cc

@@ -0,0 +1,38 @@
+#include <kernel/task/readyqueue.hpp>
+
+#include <list>
+
+#include <types/lock.hpp>
+
+#include <kernel/task/thread.hpp>
+
+using namespace kernel::task;
+
+static types::mutex dispatcher_mtx;
+static std::list<thread*> dispatcher_thds;
+
+void dispatcher::enqueue(thread* thd)
+{
+    types::lock_guard lck(dispatcher_mtx);
+
+    dispatcher_thds.push_back(thd);
+}
+
+void dispatcher::dequeue(thread* thd)
+{
+    types::lock_guard lck(dispatcher_mtx);
+
+    dispatcher_thds.remove(thd);
+}
+
+thread* dispatcher::next()
+{
+    types::lock_guard lck(dispatcher_mtx);
+
+    auto* retval = dispatcher_thds.front();
+
+    dispatcher_thds.pop_front();
+    dispatcher_thds.push_back(retval);
+
+    return retval;
+}

+ 174 - 0
src/kernel/task/thread.cc

@@ -0,0 +1,174 @@
+#include <kernel/task/thread.hpp>
+
+#include <queue>
+
+#include <types/lock.hpp>
+
+#include <kernel/log.hpp>
+#include <kernel/mm.hpp>
+#include <kernel/signal.hpp>
+#include <kernel/task/readyqueue.hpp>
+
+using namespace kernel::task;
+
+thread::thread(types::string<> name, pid_t owner)
+    : owner { owner }, attr { READY | SYSTEM }, name { name }
+{
+}
+
+thread::thread(const thread& val, pid_t owner)
+    : owner { owner }, attr { val.attr }, name { val.name }
+{
+}
+
+tid_t thread::tid() const
+{
+    return (tid_t)kstack.stack_base;
+}
+
+bool thread::operator<(const thread& rhs) const
+{
+    return tid() < rhs.tid();
+}
+
+bool thread::operator==(const thread& rhs) const
+{
+    return tid() == rhs.tid();
+}
+
+static std::priority_queue<std::byte*> s_kstacks;
+
+thread::kernel_stack::kernel_stack()
+{
+    static int allocated;
+    static types::mutex mtx;
+    types::lock_guard lck(mtx);
+
+    if (!s_kstacks.empty()) {
+        stack_base = s_kstacks.top();
+        esp = (uint32_t*)stack_base;
+        s_kstacks.pop();
+        return;
+    }
+
+    // kernel stack pt is at page#0x00005
+    kernel::paccess pa(0x00005);
+    auto pt = (pt_t)pa.ptr();
+    assert(pt);
+
+    int cnt = THREAD_KERNEL_STACK_SIZE / PAGE_SIZE;
+    pte_t* pte = *pt + allocated * cnt;
+
+    for (int i = 0; i < cnt; ++i) {
+        pte[i].v = 0x3;
+        pte[i].in.page = __alloc_raw_page();
+    }
+
+    stack_base = (std::byte*)(0xffc00000 + THREAD_KERNEL_STACK_SIZE * (allocated + 1));
+    esp = (uint32_t*)stack_base;
+
+    ++allocated;
+}
+
+thread::kernel_stack::kernel_stack(const kernel_stack& other)
+    : kernel_stack()
+{
+    auto offset = vptrdiff(other.stack_base, other.esp);
+    esp = (uint32_t*)(stack_base - offset);
+    memcpy(esp, other.esp, offset);
+}
+
+thread::kernel_stack::kernel_stack(kernel_stack&& other)
+    : stack_base(std::exchange(other.stack_base, nullptr))
+    , esp(std::exchange(other.esp, nullptr)) { }
+
+thread::kernel_stack::~kernel_stack()
+{
+    s_kstacks.push(stack_base);
+}
+
+void thread::set_attr(thd_attr_t new_attr)
+{
+    switch (new_attr) {
+    case SYSTEM:
+        attr |= SYSTEM;
+        break;
+    case READY:
+        if (attr & ZOMBIE) {
+            kmsgf("[kernel:warn] zombie process pid%d tries to wake up", owner);
+            break;
+        }
+
+        if (attr & READY) {
+            kmsgf("[kernel:warn] pid%d tries to wake up from ready state", owner);
+            break;
+        }
+
+        attr &= SYSTEM;
+        attr |= READY;
+
+        dispatcher::enqueue(this);
+        break;
+    case ISLEEP:
+        attr &= SYSTEM;
+        attr |= ISLEEP;
+
+        dispatcher::dequeue(this);
+        break;
+    case STOPPED:
+        attr &= SYSTEM;
+        attr |= STOPPED;
+
+        dispatcher::dequeue(this);
+        break;
+    case ZOMBIE:
+        attr &= SYSTEM;
+        attr |= ZOMBIE;
+
+        dispatcher::dequeue(this);
+        break;
+    default:
+        kmsgf("[kernel:warn] unknown thread attribute: %x", new_attr);
+        break;
+    }
+}
+
+void thread::send_signal(signal_list::signo_type signal)
+{
+    if (signals.raise(signal))
+        this->set_attr(READY);
+}
+
+int thread::set_thread_area(kernel::user::user_desc* ptr)
+{
+    if (ptr->read_exec_only && ptr->seg_not_present) {
+        void* dst = (void*)ptr->base_addr;
+        std::size_t len = ptr->limit;
+        if (len > 0 && dst)
+            memset(dst, 0x00, len);
+        return 0;
+    }
+
+    if (ptr->entry_number == -1U)
+        ptr->entry_number = 6;
+    else
+        return -1;
+
+    tls_desc.limit_low = ptr->limit & 0xFFFF;
+    tls_desc.base_low = ptr->base_addr & 0xFFFF;
+    tls_desc.base_mid = (ptr->base_addr >> 16) & 0xFF;
+    tls_desc.access = SD_TYPE_DATA_USER;
+    tls_desc.limit_high = (ptr->limit >> 16) & 0xF;
+    tls_desc.flags = (ptr->limit_in_pages << 3) | (ptr->seg_32bit << 2);
+    tls_desc.base_high = (ptr->base_addr >> 24) & 0xFF;
+
+    return 0;
+}
+
+int thread::load_thread_area() const
+{
+    if (tls_desc.flags == 0)
+        return -1;
+    kernel::user::load_thread_area(tls_desc);
+    return 0;
+}

+ 4 - 6
src/kernel/tty.cpp

@@ -6,7 +6,6 @@
 
 #include <types/lock.hpp>
 
-#include <kernel/event/evtqueue.hpp>
 #include <kernel/hw/serial.h>
 #include <kernel/process.hpp>
 #include <kernel/tty.hpp>
@@ -64,11 +63,10 @@ size_t tty::read(char* buf, size_t buf_size, size_t n)
         if (n == 0)
             break;
 
-        auto& mtx = this->m_cv.mtx();
-        types::lock_guard lck(mtx);
+        types::lock_guard lck(this->mtx_buf);
 
         if (this->buf.empty()) {
-            bool interrupted = !this->m_cv.wait(mtx);
+            bool interrupted = this->waitlist.wait(this->mtx_buf);
 
             if (interrupted)
                 break;
@@ -136,7 +134,7 @@ void tty::_real_commit_char(int c)
             this->_echo_char(c);
 
         if (TERMIOS_LSET(this->termio, ICANON))
-            this->m_cv.notify();
+            this->waitlist.notify_all();
 
         break;
 
@@ -210,7 +208,7 @@ void tty::commit_char(int c)
     // if handled, the character is discarded
     if (TERMIOS_LSET(this->termio, ICANON)) {
         if (TERMIOS_TESTCC(c, this->termio, VEOF)) {
-            this->m_cv.notify();
+            this->waitlist.notify_all();
             return;
         }
 

+ 14 - 14
src/kernel/vfs.cpp

@@ -730,28 +730,27 @@ fs::pipe::pipe(void)
 
 void fs::pipe::close_read(void)
 {
-    {
-        types::lock_guard lck(m_cv.mtx());
+    if (1) {
+        types::lock_guard lck(mtx);
         flags &= (~READABLE);
     }
-    m_cv.notify_all();
+    waitlist.notify_all();
 }
 
 void fs::pipe::close_write(void)
 {
-    {
-        types::lock_guard lck(m_cv.mtx());
+    if (1) {
+        types::lock_guard lck(mtx);
         flags &= (~WRITABLE);
     }
-    m_cv.notify_all();
+    waitlist.notify_all();
 }
 
 int fs::pipe::write(const char* buf, size_t n)
 {
     // TODO: check privilege
     // TODO: check EPIPE
-    {
-        auto& mtx = m_cv.mtx();
+    if (1) {
         types::lock_guard lck(mtx);
 
         if (!is_readable()) {
@@ -760,7 +759,8 @@ int fs::pipe::write(const char* buf, size_t n)
         }
 
         while (this->buf.avail() < n) {
-            if (!m_cv.wait(mtx))
+            bool interrupted = waitlist.wait(mtx);
+            if (interrupted)
                 return -EINTR;
 
             if (!is_readable()) {
@@ -773,15 +773,14 @@ int fs::pipe::write(const char* buf, size_t n)
             this->buf.put(*(buf++));
     }
 
-    m_cv.notify();
+    waitlist.notify_all();
     return n;
 }
 
 int fs::pipe::read(char* buf, size_t n)
 {
     // TODO: check privilege
-    {
-        auto& mtx = m_cv.mtx();
+    if (1) {
         types::lock_guard lck(mtx);
 
         if (!is_writeable()) {
@@ -793,7 +792,8 @@ int fs::pipe::read(char* buf, size_t n)
         }
 
         while (this->buf.size() < n) {
-            if (!m_cv.wait(mtx))
+            bool interrupted = waitlist.wait(mtx);
+            if (interrupted)
                 return -EINTR;
 
             if (!is_writeable()) {
@@ -809,7 +809,7 @@ int fs::pipe::read(char* buf, size_t n)
             *(buf++) = this->buf.get();
     }
 
-    m_cv.notify();
+    waitlist.notify_all();
     return n;
 }
 

+ 0 - 1
src/kinit.cpp

@@ -9,7 +9,6 @@
 #include <types/status.h>
 #include <types/types.h>
 
-#include <kernel/event/event.h>
 #include <kernel/hw/keyboard.h>
 #include <kernel/hw/pci.hpp>
 #include <kernel/hw/serial.h>