#include "thread/Thread.h" #include "Log.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" #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(u8 _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 && 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(); } } state = ThreadState::Exited; } status = _status; kernel_yield(); unreachable(); } enum class DefaultSignalAction { Ignore, Terminate, }; static constexpr DefaultSignalAction default_actions[] = { DefaultSignalAction::Terminate // SIGABRT }; void Thread::process_pending_signals(Registers* current_regs) { for (int i = 0; i < NSIG; i++) { if (signal_mask & (1 << i)) continue; if (pending_signals & (1 << i)) { int signo = i + 1; pending_signals &= ~(1 << i); kinfoln("signal: executing signal %d for thread %ld", signo, id); auto handler = signal_handlers[i]; if (handler.sa_handler == SIG_IGN) { kinfoln("signal: ignoring signal (handler=SIG_IGN)"); return; } if (handler.sa_handler == SIG_DFL) { 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(255); } } // 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 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; }