From 16812f0f9832769330e41421f9057b0b5a8b7b61 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 22 Dec 2019 11:35:02 +0100 Subject: [PATCH] Kernel: Get rid of "main thread" concept The idea of all processes reliably having a main thread was nice in some ways, but cumbersome in others. More importantly, it didn't match up with POSIX thread semantics, so let's move away from it. This thread gets rid of Process::main_thread() and you now we just have a bunch of Thread objects floating around each Process. When the finalizer nukes the last Thread in a Process, it will also tear down the Process. There's a bunch of more things to fix around this, but this is where we get started :^) --- Kernel/Process.cpp | 147 +++++++++++++++++++++---------------------- Kernel/Process.h | 20 +++--- Kernel/Scheduler.cpp | 17 ++--- Kernel/Scheduler.h | 1 + Kernel/Thread.cpp | 24 ++++--- Kernel/init.cpp | 27 +++++--- 6 files changed, 123 insertions(+), 113 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index c3b356e4be..4dac50b64f 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -407,7 +407,8 @@ int Process::sys$gethostname(char* buffer, ssize_t size) pid_t Process::sys$fork(RegisterDump& regs) { - auto* child = new Process(m_name, m_uid, m_gid, m_pid, m_ring, m_cwd, m_executable, m_tty, this); + Thread* child_first_thread = nullptr; + auto* child = new Process(child_first_thread, m_name, m_uid, m_gid, m_pid, m_ring, m_cwd, m_executable, m_tty, this); #ifdef FORK_DEBUG dbgprintf("fork: child=%p\n", child); @@ -427,7 +428,7 @@ pid_t Process::sys$fork(RegisterDump& regs) for (auto gid : m_gids) child->m_gids.set(gid); - auto& child_tss = child->main_thread().m_tss; + auto& child_tss = child_first_thread->m_tss; child_tss.eax = 0; // fork() returns 0 in the child :^) child_tss.ebx = regs.ebx; child_tss.ecx = regs.ecx; @@ -457,8 +458,7 @@ pid_t Process::sys$fork(RegisterDump& regs) kprintf("Process %u (%s) forked from %u @ %p\n", child->pid(), child->name().characters(), m_pid, child_tss.eip); #endif - child->main_thread().set_state(Thread::State::Skip1SchedulerPass); - + child_first_thread->set_state(Thread::State::Skip1SchedulerPass); return child->pid(); } @@ -609,9 +609,21 @@ int Process::do_exec(String path, Vector arguments, Vector envir } } + // FIXME: Should we just make a new Thread here instead? + Thread* new_main_thread = nullptr; + if (¤t->process() == this) { + new_main_thread = current; + } else { + for_each_thread([&](auto& thread) { + new_main_thread = &thread; + return IterationDecision::Break; + }); + } + ASSERT(new_main_thread); + // NOTE: We create the new stack before disabling interrupts since it will zero-fault // and we don't want to deal with faults after this point. - u32 new_userspace_esp = main_thread().make_userspace_stack_for_main_thread(move(arguments), move(environment)); + u32 new_userspace_esp = new_main_thread->make_userspace_stack_for_main_thread(move(arguments), move(environment)); // We cli() manually here because we don't want to get interrupted between do_exec() and Schedule::yield(). // The reason is that the task redirection we've set up above will be clobbered by the timer IRQ. @@ -621,39 +633,40 @@ int Process::do_exec(String path, Vector arguments, Vector envir // NOTE: Be careful to not trigger any page faults below! - Scheduler::prepare_to_modify_tss(main_thread()); + Scheduler::prepare_to_modify_tss(*new_main_thread); m_name = parts.take_last(); - main_thread().set_name(m_name); + new_main_thread->set_name(m_name); - // ss0 sp!!!!!!!!! - u32 old_esp0 = main_thread().m_tss.esp0; + auto& tss = new_main_thread->m_tss; + + u32 old_esp0 = tss.esp0; m_master_tls_size = master_tls_size; m_master_tls_alignment = master_tls_alignment; - main_thread().make_thread_specific_region({}); + new_main_thread->make_thread_specific_region({}); - memset(&main_thread().m_tss, 0, sizeof(main_thread().m_tss)); - main_thread().m_tss.eflags = 0x0202; - main_thread().m_tss.eip = entry_eip; - main_thread().m_tss.cs = 0x1b; - main_thread().m_tss.ds = 0x23; - main_thread().m_tss.es = 0x23; - main_thread().m_tss.fs = 0x23; - main_thread().m_tss.gs = thread_specific_selector() | 3; - main_thread().m_tss.ss = 0x23; - main_thread().m_tss.cr3 = page_directory().cr3(); - main_thread().m_tss.esp = new_userspace_esp; - main_thread().m_tss.ss0 = 0x10; - main_thread().m_tss.esp0 = old_esp0; - main_thread().m_tss.ss2 = m_pid; + memset(&tss, 0, sizeof(TSS32)); + tss.eflags = 0x0202; + tss.eip = entry_eip; + tss.cs = 0x1b; + tss.ds = 0x23; + tss.es = 0x23; + tss.fs = 0x23; + tss.gs = thread_specific_selector() | 3; + tss.ss = 0x23; + tss.cr3 = page_directory().cr3(); + tss.esp = new_userspace_esp; + tss.ss0 = 0x10; + tss.esp0 = old_esp0; + tss.ss2 = m_pid; #ifdef TASK_DEBUG - kprintf("Process %u (%s) exec'd %s @ %p\n", pid(), name().characters(), path.characters(), main_thread().tss().eip); + kprintf("Process %u (%s) exec'd %s @ %p\n", pid(), name().characters(), path.characters(), tss.eip); #endif - main_thread().set_state(Thread::State::Skip1SchedulerPass); + new_main_thread->set_state(Thread::State::Skip1SchedulerPass); big_lock().unlock_if_locked(); return 0; } @@ -786,7 +799,7 @@ int Process::sys$execve(const char* filename, const char** argv, const char** en return rc; } -Process* Process::create_user_process(const String& path, uid_t uid, gid_t gid, pid_t parent_pid, int& error, Vector&& arguments, Vector&& environment, TTY* tty) +Process* Process::create_user_process(Thread*& first_thread, const String& path, uid_t uid, gid_t gid, pid_t parent_pid, int& error, Vector&& arguments, Vector&& environment, TTY* tty) { // FIXME: Don't split() the path twice (sys$spawn also does it...) auto parts = path.split('/'); @@ -803,7 +816,7 @@ Process* Process::create_user_process(const String& path, uid_t uid, gid_t gid, if (!cwd) cwd = VFS::the().root_custody(); - auto* process = new Process(parts.take_last(), uid, gid, parent_pid, Ring3, move(cwd), nullptr, tty); + auto* process = new Process(first_thread, parts.take_last(), uid, gid, parent_pid, Ring3, move(cwd), nullptr, tty); error = process->exec(path, move(arguments), move(environment)); if (error != 0) { @@ -816,30 +829,30 @@ Process* Process::create_user_process(const String& path, uid_t uid, gid_t gid, g_processes->prepend(process); } #ifdef TASK_DEBUG - kprintf("Process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->main_thread().tss().eip); + kprintf("Process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), first_thread->tss().eip); #endif error = 0; return process; } -Process* Process::create_kernel_process(String&& name, void (*e)()) +Process* Process::create_kernel_process(Thread*& first_thread, String&& name, void (*e)()) { - auto* process = new Process(move(name), (uid_t)0, (gid_t)0, (pid_t)0, Ring0); - process->main_thread().tss().eip = (u32)e; + auto* process = new Process(first_thread, move(name), (uid_t)0, (gid_t)0, (pid_t)0, Ring0); + first_thread->tss().eip = (u32)e; if (process->pid() != 0) { InterruptDisabler disabler; g_processes->prepend(process); #ifdef TASK_DEBUG - kprintf("Kernel process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->main_thread().tss().eip); + kprintf("Kernel process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), first_thread->tss().eip); #endif } - process->main_thread().set_state(Thread::State::Runnable); + first_thread->set_state(Thread::State::Runnable); return process; } -Process::Process(const String& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring, RefPtr cwd, RefPtr executable, TTY* tty, Process* fork_parent) +Process::Process(Thread*& first_thread, const String& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring, RefPtr cwd, RefPtr executable, TTY* tty, Process* fork_parent) : m_name(move(name)) , m_pid(next_pid++) // FIXME: RACE: This variable looks racy! , m_uid(uid) @@ -861,9 +874,9 @@ Process::Process(const String& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel // NOTE: fork() doesn't clone all threads; the thread that called fork() becomes the main thread in the new process. if (fork_parent) - m_main_thread = current->clone(*this); + first_thread = current->clone(*this); else - m_main_thread = new Thread(*this); + first_thread = new Thread(*this); m_gids.set(m_gid); @@ -906,17 +919,8 @@ Process::Process(const String& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel Process::~Process() { - dbgprintf("~Process{%p} name=%s pid=%d, m_fds=%d\n", this, m_name.characters(), pid(), m_fds.size()); - delete m_main_thread; - m_main_thread = nullptr; - - Vector my_threads; - for_each_thread([&my_threads](auto& thread) { - my_threads.append(&thread); - return IterationDecision::Continue; - }); - for (auto* thread : my_threads) - delete thread; + dbgprintf("~Process{%p} name=%s pid=%d, m_fds=%d, m_thread_count=%u\n", this, m_name.characters(), pid(), m_fds.size(), m_thread_count); + ASSERT(thread_count() == 0); } void Process::dump_regions() @@ -1782,7 +1786,7 @@ int Process::reap(Process& process) } } - dbgprintf("reap: %s(%u) {%s}\n", process.name().characters(), process.pid(), process.main_thread().state_string()); + dbgprintf("reap: %s(%u)\n", process.name().characters(), process.pid()); ASSERT(process.is_dead()); g_processes->remove(&process); } @@ -1854,7 +1858,7 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) if (waitee_process->is_dead()) { exit_status = reap(*waitee_process); } else { - ASSERT(waitee_process->main_thread().state() == Thread::State::Stopped); + ASSERT(waitee_process->any_thread().state() == Thread::State::Stopped); exit_status = 0x7f; } return waitee_pid; @@ -2438,7 +2442,7 @@ void Process::finalize() InterruptDisabler disabler; if (auto* parent_process = Process::from_pid(m_ppid)) { // FIXME(Thread): What should we do here? Should we look at all threads' signal actions? - if (parent_process->main_thread().m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) { + if (parent_process->thread_count() && parent_process->any_thread().m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) { // NOTE: If the parent doesn't care about this process, let it go. m_ppid = 0; } else { @@ -2465,6 +2469,7 @@ void Process::die() // Tell the threads to unwind and die. InterruptDisabler disabler; for_each_thread([](Thread& thread) { + kprintf("Mark PID %u TID %u for death\n", thread.pid(), thread.tid()); thread.set_should_die(); return IterationDecision::Continue; }); @@ -2750,7 +2755,7 @@ int Process::sys$sched_setparam(pid_t pid, const struct sched_param* param) if (param->sched_priority < (int)ThreadPriority::First || param->sched_priority > (int)ThreadPriority::Last) return -EINVAL; - peer->main_thread().set_priority((ThreadPriority)param->sched_priority); + peer->any_thread().set_priority((ThreadPriority)param->sched_priority); return 0; } @@ -2770,7 +2775,7 @@ int Process::sys$sched_getparam(pid_t pid, struct sched_param* param) if (!is_superuser() && m_euid != peer->m_uid && m_uid != peer->m_uid) return -EPERM; - param->sched_priority = (int)peer->main_thread().priority(); + param->sched_priority = (int)peer->any_thread().priority(); return 0; } @@ -2976,17 +2981,7 @@ void Process::terminate_due_to_signal(u8 signal) void Process::send_signal(u8 signal, Process* sender) { // FIXME(Thread): Find the appropriate thread to deliver the signal to. - main_thread().send_signal(signal, sender); -} - -int Process::thread_count() const -{ - int count = 0; - for_each_thread([&count](auto&) { - ++count; - return IterationDecision::Continue; - }); - return count; + any_thread().send_signal(signal, sender); } int Process::sys$create_thread(void* (*entry)(void*), void* argument, const Syscall::SC_create_thread_params* params) @@ -3045,13 +3040,8 @@ int Process::sys$create_thread(void* (*entry)(void*), void* argument, const Sysc void Process::sys$exit_thread(void* exit_value) { - current->m_exit_value = exit_value; cli(); - if (¤t->process().main_thread() == current) { - // FIXME: For POSIXy reasons, we should only sys$exit once *all* threads have exited. - sys$exit(0); - return; - } + current->m_exit_value = exit_value; current->set_should_die(); big_lock().unlock_if_locked(); current->die_if_needed(); @@ -3147,13 +3137,7 @@ int Process::sys$set_thread_name(int tid, const char* buffer, int buffer_size) if (!thread) return -ESRCH; - // Forbid renaming the main thread of a process - // That guy should always be named after the process - if (thread == ¤t->process().main_thread()) - return -EINVAL; - thread->set_name({ buffer, (size_t)buffer_size }); - return 0; } int Process::sys$get_thread_name(int tid, char* buffer, int buffer_size) @@ -3767,3 +3751,14 @@ void* Process::sys$get_kernel_info_page() { return s_info_page_address_for_userspace.as_ptr(); } + +Thread& Process::any_thread() +{ + Thread* found_thread = nullptr; + for_each_thread([&](auto& thread) { + found_thread = &thread; + return IterationDecision::Break; + }); + ASSERT(found_thread); + return *found_thread; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 400d81573b..bcd9053182 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -37,8 +37,8 @@ class Process : public InlineLinkedListNode friend class Thread; public: - static Process* create_kernel_process(String&& name, void (*entry)()); - static Process* create_user_process(const String& path, uid_t, gid_t, pid_t ppid, int& error, Vector&& arguments = Vector(), Vector&& environment = Vector(), TTY* = nullptr); + static Process* create_kernel_process(Thread*& first_thread, String&& name, void (*entry)()); + static Process* create_user_process(Thread*& first_thread, const String& path, uid_t, gid_t, pid_t ppid, int& error, Vector&& arguments = Vector(), Vector&& environment = Vector(), TTY* = nullptr); ~Process(); static Vector all_pids(); @@ -56,9 +56,6 @@ public: bool is_dead() const { return m_dead; } - Thread& main_thread() { return *m_main_thread; } - const Thread& main_thread() const { return *m_main_thread; } - bool is_ring0() const { return m_ring == Ring0; } bool is_ring3() const { return m_ring == Ring3; } @@ -297,7 +294,9 @@ public: void terminate_due_to_signal(u8 signal); void send_signal(u8, Process* sender); - int thread_count() const; + u16 thread_count() const { return m_thread_count; } + + Thread& any_thread(); Lock& big_lock() { return m_big_lock; } @@ -310,7 +309,7 @@ private: friend class Scheduler; friend class Region; - Process(const String& name, uid_t, gid_t, pid_t ppid, RingLevel, RefPtr cwd = nullptr, RefPtr executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr); + Process(Thread*& first_thread, const String& name, uid_t, gid_t, pid_t ppid, RingLevel, RefPtr cwd = nullptr, RefPtr executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr); Range allocate_range(VirtualAddress, size_t); @@ -325,8 +324,6 @@ private: KResult do_kill(Process&, int signal); KResult do_killpg(pid_t pgrp, int signal); - Thread* m_main_thread { nullptr }; - RefPtr m_page_directory; Process* m_prev { nullptr }; @@ -356,6 +353,7 @@ private: RingLevel m_ring { Ring0 }; u8 m_termination_status { 0 }; u8 m_termination_signal { 0 }; + u16 m_thread_count { 0 }; bool m_being_inspected { false }; bool m_dead { false }; @@ -465,8 +463,8 @@ inline void Process::for_each_thread(Callback callback) const pid_t my_pid = pid(); if (my_pid == 0) { - // NOTE: Special case the colonel thread, since its main thread is not in the global thread table. - callback(const_cast(main_thread())); + // NOTE: Special case the colonel process, since its main thread is not in the global thread table. + callback(*g_colonel); return; } diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index ef710619fd..99a2ccd920 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -47,6 +47,7 @@ static u32 time_slice_for(ThreadPriority priority) Thread* current; Thread* g_last_fpu_thread; Thread* g_finalizer; +Thread* g_colonel; WaitQueue* g_finalizer_wait_queue; static Process* s_colonel_process; u64 g_uptime; @@ -222,7 +223,7 @@ bool Thread::WaitBlocker::should_unblock(Thread& thread, time_t, long) return IterationDecision::Continue; bool child_exited = child.is_dead(); - bool child_stopped = child.main_thread().state() == Thread::State::Stopped; + bool child_stopped = child.thread_count() && child.any_thread().state() == Thread::State::Stopped; bool wait_finished = ((m_wait_options & WEXITED) && child_exited) || ((m_wait_options & WSTOPPED) && child_stopped); @@ -288,7 +289,7 @@ bool Scheduler::pick_next() if (!current) { // XXX: The first ever context_switch() goes to the idle process. // This to setup a reliable place we can return to. - return context_switch(s_colonel_process->main_thread()); + return context_switch(*g_colonel); } struct timeval now; @@ -305,7 +306,7 @@ bool Scheduler::pick_next() Process::for_each([&](Process& process) { if (process.is_dead()) { - if (current != &process.main_thread() && (!process.ppid() || !Process::from_pid(process.ppid()))) { + if (current->pid() != process.pid() && (!process.ppid() || !Process::from_pid(process.ppid()))) { auto name = process.name(); auto pid = process.pid(); auto exit_status = Process::reap(process); @@ -369,7 +370,7 @@ bool Scheduler::pick_next() auto& runnable_list = g_scheduler_data->m_runnable_threads; if (runnable_list.is_empty()) - return context_switch(s_colonel_process->main_thread()); + return context_switch(*g_colonel); auto* previous_head = runnable_list.first(); for (;;) { @@ -386,7 +387,7 @@ bool Scheduler::pick_next() if (thread == previous_head) { // Back at process_head, nothing wants to run. Send in the colonel! - return context_switch(s_colonel_process->main_thread()); + return context_switch(*g_colonel); } } } @@ -535,9 +536,9 @@ void Scheduler::initialize() g_finalizer_wait_queue = new WaitQueue; s_redirection.selector = gdt_alloc_entry(); initialize_redirection(); - s_colonel_process = Process::create_kernel_process("colonel", nullptr); + s_colonel_process = Process::create_kernel_process(g_colonel, "colonel", nullptr); // Make sure the colonel uses a smallish time slice. - s_colonel_process->main_thread().set_priority(ThreadPriority::Idle); + g_colonel->set_priority(ThreadPriority::Idle); load_task_register(s_redirection.selector); } @@ -614,7 +615,7 @@ static bool s_should_stop_idling = false; void Scheduler::stop_idling() { - if (current != &s_colonel_process->main_thread()) + if (current != g_colonel) return; s_should_stop_idling = true; diff --git a/Kernel/Scheduler.h b/Kernel/Scheduler.h index bbf086a107..25841c4030 100644 --- a/Kernel/Scheduler.h +++ b/Kernel/Scheduler.h @@ -14,6 +14,7 @@ struct SchedulerData; extern Thread* current; extern Thread* g_last_fpu_thread; extern Thread* g_finalizer; +extern Thread* g_colonel; extern WaitQueue* g_finalizer_wait_queue; extern u64 g_uptime; extern SchedulerData* g_scheduler_data; diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 44f7daf171..843ecc18ac 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -46,6 +46,7 @@ Thread::Thread(Process& process) , m_tid(process.m_next_tid++) , m_name(process.name()) { + process.m_thread_count++; dbgprintf("Thread{%p}: New thread TID=%u in %s(%u)\n", this, m_tid, process.name().characters(), process.pid()); set_default_signal_dispositions(); m_fpu_state = (FPUState*)kmalloc_aligned(sizeof(FPUState), 16); @@ -121,6 +122,9 @@ Thread::~Thread() if (m_userspace_stack_region) m_process.deallocate_region(*m_userspace_stack_region); + + ASSERT(m_process.m_thread_count); + m_process.m_thread_count--; } void Thread::unblock() @@ -135,8 +139,10 @@ void Thread::unblock() void Thread::set_should_die() { - if (m_should_die) + if (m_should_die) { + dbgprintf("Should already die (%u)\n", m_tid); return; + } InterruptDisabler disabler; // Remember that we should die instead of returning to @@ -258,13 +264,6 @@ void Thread::finalize() if (m_dump_backtrace_on_finalization) dbg() << backtrace_impl(); - - if (this == &m_process.main_thread()) { - m_process.finalize(); - return; - } - - delete this; } void Thread::finalize_dying_threads() @@ -278,8 +277,15 @@ void Thread::finalize_dying_threads() return IterationDecision::Continue; }); } - for (auto* thread : dying_threads) + dbgprintf("Finalizing %u dying threads\n", dying_threads.size()); + for (auto* thread : dying_threads) { + auto& process = thread->process(); thread->finalize(); + delete thread; + if (process.m_thread_count == 0) + process.finalize(); + } + dbgprintf("Done\n"); } bool Thread::tick() diff --git a/Kernel/init.cpp b/Kernel/init.cpp index cd3c652a36..803f3d065e 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -169,22 +169,27 @@ VFS* vfs; // accept keyboard input. if (text_debug) { tty0->set_graphical(false); - auto* shell_process = Process::create_user_process("/bin/Shell", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0); + Thread* thread = nullptr; + Process::create_user_process(thread, "/bin/Shell", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0); if (error != 0) { kprintf("init_stage2: error spawning Shell: %d\n", error); hang(); } - shell_process->main_thread().set_priority(ThreadPriority::High); + thread->set_priority(ThreadPriority::High); } else { tty0->set_graphical(true); - auto* system_server_process = Process::create_user_process("/bin/SystemServer", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0); + Thread* thread = nullptr; + Process::create_user_process(thread, "/bin/SystemServer", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0); if (error != 0) { kprintf("init_stage2: error spawning SystemServer: %d\n", error); hang(); } - system_server_process->main_thread().set_priority(ThreadPriority::High); + thread->set_priority(ThreadPriority::High); + } + { + Thread* thread = nullptr; + Process::create_kernel_process(thread, "NetworkTask", NetworkTask_main); } - Process::create_kernel_process("NetworkTask", NetworkTask_main); current->process().sys$exit(0); ASSERT_NOT_REACHED(); @@ -308,15 +313,19 @@ extern "C" [[noreturn]] void init(u32 physical_address_for_kernel_page_tables) Process::initialize(); Thread::initialize(); - Process::create_kernel_process("init_stage2", init_stage2); - Process::create_kernel_process("syncd", [] { + + Thread* init_stage2_thread = nullptr; + Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2); + + Thread* syncd_thread = nullptr; + Process::create_kernel_process(syncd_thread, "syncd", [] { for (;;) { VFS::the().sync(); current->sleep(1 * TICKS_PER_SECOND); } }); - Process::create_kernel_process("Finalizer", [] { - g_finalizer = current; + + Process::create_kernel_process(g_finalizer, "Finalizer", [] { current->set_priority(ThreadPriority::Low); for (;;) { current->wait_on(*g_finalizer_wait_queue);