diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 1b1007c8cb..50dbca98c9 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -51,6 +51,9 @@ namespace Kernel BAN::ErrorOr fork(uintptr_t rsp, uintptr_t rip); BAN::ErrorOr exec(BAN::StringView path, const char* const* argv, const char* const* envp); + int block_until_exit(); + BAN::ErrorOr wait(pid_t pid, int* stat_loc, int options); + BAN::ErrorOr open(BAN::StringView, int); BAN::ErrorOr close(int fd); BAN::ErrorOr read(int fd, void* buffer, size_t count); @@ -105,6 +108,15 @@ namespace Kernel OpenFileDescription& open_file_description(int); BAN::ErrorOr get_free_fd(); + + struct ExitStatus + { + Semaphore semaphore; + int exit_code { 0 }; + bool exited { false }; + int waiting { 0 }; + }; + BAN::Vector m_open_files; BAN::Vector m_mapped_ranges; @@ -118,6 +130,7 @@ namespace Kernel BAN::UniqPtr m_general_allocator; userspace_entry_t m_userspace_entry; + ExitStatus m_exit_status; BAN::UniqPtr m_page_table; TTY* m_tty { nullptr }; diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 23325dd6f6..c135a42018 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -117,6 +117,15 @@ namespace Kernel void Process::exit() { m_lock.lock(); + m_exit_status.exited = true; + while (m_exit_status.waiting > 0) + { + m_exit_status.semaphore.unblock(); + m_lock.unlock(); + // TODO: add proper software reschedule + Scheduler::get().set_current_thread_sleeping(0); + } + m_lock.lock(); m_threads.clear(); for (auto& open_fd : m_open_files) @@ -249,6 +258,52 @@ namespace Kernel ASSERT_NOT_REACHED(); } + int Process::block_until_exit() + { + ASSERT(s_process_lock.is_locked()); + ASSERT(this != &Process::current()); + + s_process_lock.unlock(); + + m_lock.lock(); + m_exit_status.waiting++; + while (!m_exit_status.exited) + { + m_lock.unlock(); + m_exit_status.semaphore.block(); + m_lock.lock(); + } + + int ret = m_exit_status.exit_code; + m_exit_status.waiting--; + m_lock.unlock(); + + s_process_lock.lock(); + + return ret; + } + + BAN::ErrorOr Process::wait(pid_t pid, int* stat_loc, int options) + { + Process* target = nullptr; + + // FIXME: support options + if (options) + return BAN::Error::from_errno(EINVAL); + + LockGuard _(s_process_lock); + for (auto* process : s_processes) + if (process->pid() == pid) + target = process; + + if (target == nullptr) + return BAN::Error::from_errno(ECHILD); + + pid_t ret = target->pid(); + *stat_loc = target->block_until_exit(); + + return ret; + } void Process::load_elf(LibELF::ELF& elf) { diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index f7fd4012da..44427e6939 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -120,6 +120,14 @@ namespace Kernel ASSERT_NOT_REACHED(); } + long sys_wait(pid_t pid, int* stat_loc, int options) + { + auto ret = Process::current().wait(pid, stat_loc, options); + if (ret.is_error()) + return -ret.error().get_error_code(); + return ret.value(); + } + extern "C" long sys_fork_trampoline(); extern "C" long cpp_syscall_handler(int syscall, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5) @@ -182,6 +190,9 @@ namespace Kernel case SYS_EXEC: ret = sys_exec((const char*)arg1, (const char* const*)arg2, (const char* const*)arg3); break; + case SYS_WAIT: + ret = sys_wait((pid_t)arg1, (int*)arg2, (int)arg3); + break; default: Kernel::panic("Unknown syscall {}", syscall); }