Compare commits
7 Commits
c25a95f5c2
...
a0123e7c2d
Author | SHA1 | Date |
---|---|---|
|
a0123e7c2d | |
|
be786be67d | |
|
e85b18e206 | |
|
f32f62dfc1 | |
|
28392050bf | |
|
b9cc6063ff | |
|
9066e62a97 |
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -20,6 +20,7 @@ set(LIBC_SOURCES
|
|||
pthread.cpp
|
||||
pwd.cpp
|
||||
scanf_impl.cpp
|
||||
sched.cpp
|
||||
setjmp.cpp
|
||||
signal.cpp
|
||||
stdio.cpp
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#include <sched.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int sched_yield(void)
|
||||
{
|
||||
return syscall(SYS_YIELD);
|
||||
}
|
|
@ -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]);
|
||||
|
|
Loading…
Reference in New Issue