249 lines
7.1 KiB
C++
249 lines
7.1 KiB
C++
#include "thread/Thread.h"
|
|
#include "Log.h"
|
|
#include "arch/CPU.h"
|
|
#include "memory/MemoryManager.h"
|
|
#include "thread/Scheduler.h"
|
|
#include <bits/atfile.h>
|
|
#include <bits/open-flags.h>
|
|
#include <bits/signal.h>
|
|
#include <bits/waitpid.h>
|
|
#include <luna/Alloc.h>
|
|
#include <luna/Atomic.h>
|
|
#include <luna/PathParser.h>
|
|
|
|
static Atomic<pid_t> g_next_id;
|
|
|
|
LinkedList<Thread> g_threads;
|
|
|
|
void Thread::init()
|
|
{
|
|
g_next_id = 2;
|
|
}
|
|
|
|
Result<Thread*> new_thread()
|
|
{
|
|
Thread* const thread = TRY(make<Thread>());
|
|
|
|
thread->id = g_next_id++;
|
|
|
|
return thread;
|
|
}
|
|
|
|
pid_t next_thread_id()
|
|
{
|
|
return g_next_id.load();
|
|
}
|
|
|
|
Result<int> 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<FileDescriptor*> Thread::resolve_fd(int fd)
|
|
{
|
|
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
|
|
|
|
Option<FileDescriptor>& maybe_descriptor = fd_table[fd];
|
|
|
|
if (!maybe_descriptor.has_value()) return err(EBADF);
|
|
|
|
return maybe_descriptor.value_ptr();
|
|
}
|
|
|
|
Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path,
|
|
bool follow_last_symlink, SharedPtr<VFS::Inode>* 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)
|
|
{
|
|
#ifndef MOON_ENABLE_TESTING_FEATURES
|
|
if (this->id == 1) fail("the init process exited");
|
|
#else
|
|
if (this->id == 1) CPU::magic_exit(_status);
|
|
#endif
|
|
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 == id)
|
|
{
|
|
parent->child_being_waited_for = 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
|
|
DefaultSignalAction::Ignore, // SIGWINCH
|
|
};
|
|
|
|
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 %d", 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;
|
|
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_ignore_pending_signal()
|
|
{
|
|
for (int i = 0; i < NSIG; i++)
|
|
{
|
|
if (pending_signals & (1 << i))
|
|
{
|
|
int signo = i + 1;
|
|
if (signo == SIGKILL || signo == SIGSTOP) return false;
|
|
if (signal_mask & (1 << i)) continue;
|
|
auto handler = signal_handlers[i];
|
|
if (handler.sa_handler == SIG_IGN) return true;
|
|
if (handler.sa_handler == SIG_DFL && default_actions[i] == DefaultSignalAction::Ignore) return true;
|
|
return false;
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
|
|
OpenFileDescription::OpenFileDescription(SharedPtr<VFS::Inode> ino, int fl) : inode(ino), flags(fl)
|
|
{
|
|
inode->add_handle();
|
|
}
|
|
|
|
OpenFileDescription::~OpenFileDescription()
|
|
{
|
|
inode->remove_handle();
|
|
inode->did_close();
|
|
}
|
|
|
|
bool FileDescriptor::should_append()
|
|
{
|
|
return description->flags & O_APPEND;
|
|
}
|
|
|
|
bool FileDescriptor::should_block()
|
|
{
|
|
return !(description->flags & O_NONBLOCK);
|
|
}
|
|
|
|
bool FileDescriptor::is_readable()
|
|
{
|
|
return description->flags & O_RDONLY;
|
|
}
|
|
|
|
bool FileDescriptor::is_writable()
|
|
{
|
|
return description->flags & O_WRONLY;
|
|
}
|