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);