Compare commits

...

7 Commits

13 changed files with 216 additions and 20 deletions

View File

@ -36,6 +36,8 @@ start_kernel_thread:
.global start_userspace_thread
start_userspace_thread:
call load_thread_sse
call get_thread_start_sp
movl %eax, %esp

View File

@ -27,6 +27,8 @@ start_kernel_thread:
.global start_userspace_thread
start_userspace_thread:
call load_thread_sse
call get_thread_start_sp
movq %rax, %rsp

View File

@ -182,8 +182,10 @@ namespace Kernel
BAN::ErrorOr<long> sys_sigpending(sigset_t* set);
BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset);
BAN::ErrorOr<long> sys_yield();
BAN::ErrorOr<long> sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg);
BAN::ErrorOr<long> sys_pthread_exit(void* value);
BAN::ErrorOr<long> sys_pthread_join(pthread_t thread, void** value);
BAN::ErrorOr<long> sys_pthread_self();
BAN::ErrorOr<long> sys_tcgetpgrp(int fd);
@ -296,10 +298,11 @@ namespace Kernel
struct pthread_info_t
{
Thread* thread;
pthread_t thread;
void* value;
};
BAN::Vector<pthread_info_t> m_exited_pthreads;
ThreadBlocker m_pthread_exit_blocker;
uint64_t m_alarm_interval_ns { 0 };
uint64_t m_alarm_wake_time_ns { 0 };

View File

@ -354,6 +354,8 @@ namespace Kernel
asm volatile("cli; 1: hlt; jmp 1b");
}
Thread::current().save_sse();
ASSERT(InterruptController::get().is_in_service(IRQ_TIMER - IRQ_VECTOR_BASE));
InterruptController::get().eoi(IRQ_TIMER - IRQ_VECTOR_BASE);
@ -365,6 +367,8 @@ namespace Kernel
auto& current_thread = Thread::current();
if (current_thread.can_add_signal_to_execute())
current_thread.handle_signal();
Thread::current().load_sse();
}
extern "C" void cpp_irq_handler(uint32_t irq)

View File

@ -2084,6 +2084,12 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_yield()
{
Processor::yield();
return 0;
}
BAN::ErrorOr<long> Process::sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg)
{
if (attr != nullptr)
@ -2109,11 +2115,12 @@ namespace Kernel
if (&Thread::current() == m_threads.front())
return BAN::Error::from_errno(EINVAL);
TRY(m_exited_pthreads.emplace_back(&Thread::current(), value));
TRY(m_exited_pthreads.emplace_back(Thread::current().tid(), value));
for (auto* thread : m_threads)
{
if (thread != &Thread::current())
continue;
m_pthread_exit_blocker.unblock();
m_process_lock.unlock();
thread->on_exit();
}
@ -2121,6 +2128,58 @@ namespace Kernel
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<long> Process::sys_pthread_join(pthread_t thread, void** value)
{
LockGuard _(m_process_lock);
if (value)
TRY(validate_pointer_access(value, sizeof(void*), true));
if (thread == Thread::current().tid())
return BAN::Error::from_errno(EINVAL);
const auto wait_thread =
[&]() -> bool
{
for (size_t i = 0; i < m_exited_pthreads.size(); i++)
{
if (m_exited_pthreads[i].thread != thread)
continue;
if (value)
*value = m_exited_pthreads[i].value;
m_exited_pthreads.remove(i);
return true;
}
return false;
};
if (wait_thread())
return 0;
{
bool found = false;
for (auto* _thread : m_threads)
if (_thread->tid() == thread)
found = true;
if (!found)
return BAN::Error::from_errno(EINVAL);
}
for (;;)
{
{
LockFreeGuard _(m_process_lock);
m_pthread_exit_blocker.block_with_timeout_ms(100);
}
if (wait_thread())
return 0;
}
}
BAN::ErrorOr<long> Process::sys_pthread_self()
{
return Thread::current().tid();

View File

@ -34,6 +34,11 @@ namespace Kernel
return Thread::current().userspace_stack_top() - 4 * sizeof(uintptr_t);
}
extern "C" void load_thread_sse()
{
Thread::current().load_sse();
}
static pid_t s_next_tid = 1;
alignas(16) static uint8_t s_default_sse_storage[512];
@ -168,6 +173,7 @@ namespace Kernel
{
auto* thread = TRY(create_userspace(m_process, m_process->page_table()));
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
thread->setup_exec_impl(
@ -202,6 +208,7 @@ namespace Kernel
thread->m_interrupt_stack.sp = sp;
thread->m_interrupt_stack.ss = 0x10;
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
#if ARCH(x86_64)

View File

@ -20,6 +20,7 @@ set(LIBC_SOURCES
pthread.cpp
pwd.cpp
scanf_impl.cpp
sched.cpp
setjmp.cpp
signal.cpp
stdio.cpp

View File

@ -70,16 +70,16 @@ __BEGIN_DECLS
#endif
#undef __need_pthread_rwlockattr_t
#if !defined(__pthread_spinlock_t_defined) && (defined(__need_all_types) || defined(__need_pthread_spinlock_t))
#define __pthread_spinlock_t_defined 1
typedef int pthread_spinlock_t;
#endif
#undef __need_pthread_spinlock_t
#if !defined(__pthread_t_defined) && (defined(__need_all_types) || defined(__need_pthread_t))
#define __pthread_t_defined 1
typedef int pthread_t;
typedef pid_t pthread_t;
#endif
#undef __need_pthread_t
#if !defined(__pthread_spinlock_t_defined) && (defined(__need_all_types) || defined(__need_pthread_spinlock_t))
#define __pthread_spinlock_t_defined 1
typedef pthread_t pthread_spinlock_t;
#endif
#undef __need_pthread_spinlock_t
__END_DECLS

View File

@ -90,8 +90,10 @@ __BEGIN_DECLS
O(SYS_FSYNC, fsync) \
O(SYS_SYMLINKAT, symlinkat) \
O(SYS_HARDLINKAT, hardlinkat) \
O(SYS_YIELD, yield) \
O(SYS_PTHREAD_CREATE, pthread_create) \
O(SYS_PTHREAD_EXIT, pthread_exit) \
O(SYS_PTHREAD_JOIN, pthread_join) \
O(SYS_PTHREAD_SELF, pthread_self) \
enum Syscall

View File

@ -132,6 +132,10 @@ __BEGIN_DECLS
#endif
#undef __need_off_t
#ifdef __need_pthread_t
#define __need_pid_t
#endif
#if !defined(__pid_t_defined) && (defined(__need_all_types) || defined(__need_pid_t))
#define __pid_t_defined 1
typedef int pid_t;

View File

@ -1,5 +1,8 @@
#include <BAN/Assert.h>
#include <BAN/Atomic.h>
#include <BAN/PlacementNew.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
@ -12,7 +15,11 @@ struct pthread_trampoline_info_t
void* arg;
};
static void pthread_trampoline(void* arg)
// stack is 16 byte aligned on entry, this `call` is used to align it
extern "C" void pthread_trampoline(void*);
asm("pthread_trampoline: call pthread_trampoline_cpp");
extern "C" void pthread_trampoline_cpp(void* arg)
{
pthread_trampoline_info_t info;
memcpy(&info, arg, sizeof(pthread_trampoline_info_t));
@ -48,7 +55,71 @@ void pthread_exit(void* value_ptr)
ASSERT_NOT_REACHED();
}
int pthread_join(pthread_t thread, void** value_ptr)
{
return syscall(SYS_PTHREAD_JOIN, thread, value_ptr);
}
pthread_t pthread_self(void)
{
return syscall(SYS_PTHREAD_SELF);
}
static inline BAN::Atomic<pthread_t>& pthread_spin_get_atomic(pthread_spinlock_t* lock)
{
static_assert(sizeof(pthread_spinlock_t) <= sizeof(BAN::Atomic<pthread_t>));
static_assert(alignof(pthread_spinlock_t) <= alignof(BAN::Atomic<pthread_t>));
return *reinterpret_cast<BAN::Atomic<pthread_t>*>(lock);
}
int pthread_spin_destroy(pthread_spinlock_t* lock)
{
pthread_spin_get_atomic(lock).~Atomic<pthread_t>();
return 0;
}
int pthread_spin_init(pthread_spinlock_t* lock, int pshared)
{
(void)pshared;
new (lock) BAN::Atomic<pthread_t>();
pthread_spin_get_atomic(lock) = false;
return 0;
}
int pthread_spin_lock(pthread_spinlock_t* lock)
{
auto& atomic = pthread_spin_get_atomic(lock);
const pthread_t tid = pthread_self();
ASSERT(atomic.load(BAN::MemoryOrder::memory_order_relaxed) != tid);
pthread_t expected = 0;
while (!atomic.compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acquire))
{
sched_yield();
expected = 0;
}
return 0;
}
int pthread_spin_trylock(pthread_spinlock_t* lock)
{
auto& atomic = pthread_spin_get_atomic(lock);
const pthread_t tid = pthread_self();
ASSERT(atomic.load(BAN::MemoryOrder::memory_order_relaxed) != tid);
pthread_t expected = 0;
if (atomic.compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acquire))
return 0;
return EBUSY;
}
int pthread_spin_unlock(pthread_spinlock_t* lock)
{
auto& atomic = pthread_spin_get_atomic(lock);
ASSERT(atomic.load(BAN::MemoryOrder::memory_order_relaxed) == pthread_self());
atomic.store(0, BAN::MemoryOrder::memory_order_release);
return 0;
}

View File

@ -0,0 +1,8 @@
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
int sched_yield(void)
{
return syscall(SYS_YIELD);
}

View File

@ -1,10 +1,12 @@
#include <BAN/Assert.h>
#include <BAN/Debug.h>
#include <BAN/Atomic.h>
#include <BAN/Math.h>
#include <BAN/PlacementNew.h>
#include <bits/printf.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <scanf_impl.h>
#include <stdarg.h>
#include <stdio.h>
@ -31,7 +33,14 @@ struct FILE
unsigned char unget_buffer[12];
uint32_t unget_buf_idx;
// TODO: use recursive pthread_mutex when implemented?
// this storage hack is to keep FILE pod (init order)
BAN::Atomic<pthread_t>& locker() { return *reinterpret_cast<BAN::Atomic<pthread_t>*>(locker_storage); }
unsigned char locker_storage[sizeof(pthread_t)];
uint32_t lock_depth;
};
static_assert(BAN::is_pod_v<FILE>);
struct ScopeLock
{
@ -88,8 +97,14 @@ static int drop_read_buffer(FILE* file)
void _init_stdio()
{
for (size_t i = 0; i < FOPEN_MAX; i++)
{
init_closed_file(&s_files[i]);
new (&s_files[i].locker()) BAN::Atomic<pthread_t>();
s_files[i].locker() = -1;
s_files[i].lock_depth = 0;
}
s_files[STDIN_FILENO].fd = STDIN_FILENO;
s_files[STDIN_FILENO].mode = O_RDONLY;
s_files[STDIN_FILENO].buffer_type = _IOLBF;
@ -170,7 +185,6 @@ FILE* fdopen(int fd, const char* mode_str)
return nullptr;
}
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
ScopeLock _(&s_files[i]);
@ -292,9 +306,20 @@ int fileno(FILE* fp)
return fp->fd;
}
void flockfile(FILE*)
void flockfile(FILE* fp)
{
// FIXME: when threads are implemented
const pthread_t tid = pthread_self();
pthread_t expected = -1;
while (!fp->locker().compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acq_rel))
{
if (expected == tid)
break;
sched_yield();
expected = -1;
}
fp->lock_depth++;
}
FILE* fopen(const char* pathname, const char* mode_str)
@ -310,7 +335,6 @@ FILE* fopen(const char* pathname, const char* mode_str)
if (fd == -1)
return nullptr;
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
ScopeLock _(&s_files[i]);
@ -458,15 +482,25 @@ off_t ftello(FILE* file)
return ret;
}
int ftrylockfile(FILE*)
int ftrylockfile(FILE* fp)
{
// FIXME: when threads are implemented
const pthread_t tid = pthread_self();
pthread_t expected = -1;
if (!fp->locker().compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acq_rel))
if (expected != tid)
return 1;
fp->lock_depth++;
return 0;
}
void funlockfile(FILE*)
void funlockfile(FILE* fp)
{
// FIXME: when threads are implemented
ASSERT(fp->locker() == pthread_self());
ASSERT(fp->lock_depth > 0);
if (--fp->lock_depth == 0)
fp->locker().store(-1, BAN::MemoryOrder::memory_order_release);
}
size_t fwrite(const void* buffer, size_t size, size_t nitems, FILE* file)
@ -706,7 +740,6 @@ FILE* popen(const char* command, const char* mode_str)
close(read ? fds[1] : fds[0]);
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
ScopeLock _(&s_files[i]);