#include "thread/Thread.h" #include "Log.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" #include #include #include #include #include #include #include static Atomic g_next_id; LinkedList g_threads; void Thread::init() { g_next_id = 2; } Result new_thread() { Thread* const thread = TRY(make()); thread->id = g_next_id++; return thread; } Result Thread::allocate_fd(int min) { if (min < 0 || min >= FD_MAX) return err(EINVAL); for (int i = min; i < FD_MAX; i++) { // FIXME: Possible race condition if multiple threads share a FileDescriptorTable? Let's not worry about it for // now, we're still a long way away from reaching that point. if (!fd_table[i].has_value()) { return i; } } return err(EMFILE); } Result Thread::resolve_fd(int fd) { if (fd < 0 || fd >= FD_MAX) return err(EBADF); Option& maybe_descriptor = fd_table[fd]; if (!maybe_descriptor.has_value()) return err(EBADF); return maybe_descriptor.value_ptr(); } Result> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path, bool follow_last_symlink, SharedPtr* parent_inode) { if (parent_inode) *parent_inode = this->current_directory; if (PathParser::is_absolute(path.view())) return VFS::resolve_path(path.chars(), this->auth, {}, follow_last_symlink); if (dirfd == AT_FDCWD) return VFS::resolve_path(path.chars(), this->auth, this->current_directory, follow_last_symlink); auto descriptor = TRY(resolve_fd(dirfd)); if (parent_inode) *parent_inode = descriptor->inode; if (path.is_empty() && allow_empty_path) return descriptor->inode; return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink); } [[noreturn]] void Thread::exit_and_signal_parent(int _status) { if (this->id == 1) fail("the init process exited"); if (is_kernel) state = ThreadState::Dying; else { Scheduler::for_each_child(this, [](Thread* child) { child->parent = Scheduler::init_thread(); return true; }); if (parent) { if (parent->state == ThreadState::Waiting) { auto child = *parent->child_being_waited_for; if (child == -1 || child == (pid_t)id) { parent->child_being_waited_for = (pid_t)id; parent->wake_up(); } } else { parent->send_signal(SIGCHLD); } } state = ThreadState::Exited; } status = _status; kernel_yield(); unreachable(); } enum class DefaultSignalAction { Ignore, Terminate, }; // FIXME: Implement coredumps for some signals. static constexpr DefaultSignalAction default_actions[] = { DefaultSignalAction::Terminate, // SIGHUP DefaultSignalAction::Terminate, // SIGINT DefaultSignalAction::Terminate, // SIGQUIT (dump core) DefaultSignalAction::Terminate, // SIGILL (dump core) DefaultSignalAction::Terminate, // SIGTRAP (dump core) DefaultSignalAction::Terminate, // SIGABRT (dump core) DefaultSignalAction::Ignore, // SIGCHLD DefaultSignalAction::Terminate, // SIGFPE (dump core) DefaultSignalAction::Terminate, // SIGKILL DefaultSignalAction::Terminate, // SIGSTOP (FIXME: Support stopping and continuing) DefaultSignalAction::Terminate, // SIGSEGV (dump core) DefaultSignalAction::Ignore, // SIGCONT (FIXME: Support stopping and continuing) DefaultSignalAction::Terminate, // SIGPIPE DefaultSignalAction::Terminate, // SIGALRM DefaultSignalAction::Terminate, // SIGTERM DefaultSignalAction::Terminate, // SIGTTIN DefaultSignalAction::Terminate, // SIGTTOU }; void Thread::process_pending_signals(Registers* current_regs) { interrupted = false; for (int i = 0; i < NSIG; i++) { int signo = i + 1; if (signo != SIGKILL && signo != SIGSTOP && signal_mask & (1 << i)) continue; if (pending_signals & (1 << i)) { pending_signals &= ~(1 << i); kinfoln("signal: executing signal %d for thread %ld", signo, id); auto handler = signal_handlers[i]; if (signo != SIGKILL && signo != SIGSTOP && handler.sa_handler == SIG_IGN) { kinfoln("signal: ignoring signal (handler=SIG_IGN)"); return; } if (handler.sa_handler == SIG_DFL || signo == SIGKILL || signo == SIGSTOP) { default_signal: if (id == 1) { kwarnln("signal: init got a signal it has no handler for, ignoring"); return; } kinfoln("signal: using default behavior (handler=SIG_DFL)"); auto action = default_actions[i]; switch (action) { case DefaultSignalAction::Ignore: return; // FIXME: Add signal exit codes. case DefaultSignalAction::Terminate: exit_and_signal_parent(signo | _SIGBIT); default: return; } } // If we fail to deliver the signal (usually because there's not enough space on the stack), execute the // default action. if (!deliver_signal(signo, current_regs)) goto default_signal; return; } } } bool Thread::will_invoke_signal_handler() { for (int i = 0; i < NSIG; i++) { if (pending_signals & (1 << i)) { int signo = i + 1; if (signo != SIGKILL && signo != SIGSTOP && signal_mask & (1 << i)) continue; auto handler = signal_handlers[i]; if (handler.sa_handler == SIG_IGN || handler.sa_handler == SIG_DFL) return false; if (signo == SIGKILL || signo == SIGSTOP) return false; return true; } } return false; } void Thread::send_signal(int signo) { check(signo > 0 && signo <= NSIG); pending_signals |= 1 << (signo - 1); if (state == ThreadState::Waiting || state == ThreadState::Sleeping) { interrupted = true; wake_up(); } } bool FileDescriptor::should_append() { return flags & O_APPEND; } bool FileDescriptor::should_block() { return !(flags & O_NONBLOCK); } bool FileDescriptor::is_readable() { return flags & O_RDONLY; } bool FileDescriptor::is_writable() { return flags & O_WRONLY; }