Kernel/LibC: Add basic support for pthread_{create,exit}

This commit is contained in:
Bananymous 2025-04-01 23:08:27 +03:00
parent 788f5429e1
commit c1618e2b5d
9 changed files with 178 additions and 10 deletions

View File

@ -182,6 +182,9 @@ 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_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_tcgetpgrp(int fd);
BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid);
@ -290,6 +293,13 @@ namespace Kernel
BAN::Vector<Thread*> m_threads;
struct pthread_info_t
{
Thread* thread;
void* value;
};
BAN::Vector<pthread_info_t> m_exited_pthreads;
uint64_t m_alarm_interval_ns { 0 };
uint64_t m_alarm_wake_time_ns { 0 };

View File

@ -38,6 +38,8 @@ namespace Kernel
static BAN::ErrorOr<Thread*> create_userspace(Process*, PageTable&);
~Thread();
BAN::ErrorOr<Thread*> pthread_create(entry_t, void*);
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip);
void setup_exec();
void setup_process_cleanup();

View File

@ -185,6 +185,8 @@ namespace Kernel
process->m_userspace_info.envp = nullptr;
auto* thread = MUST(Thread::create_userspace(process, process->page_table()));
thread->setup_exec();
process->add_thread(thread);
process->register_to_scheduler();
return process;
@ -208,6 +210,7 @@ namespace Kernel
Process::~Process()
{
ASSERT(m_threads.empty());
ASSERT(m_exited_pthreads.empty());
ASSERT(m_mapped_regions.empty());
ASSERT(!m_page_table);
}
@ -238,6 +241,8 @@ namespace Kernel
}
}
m_exited_pthreads.clear();
ProcFileSystem::get().on_process_delete(*this);
m_process_lock.lock();
@ -2079,6 +2084,43 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg)
{
if (attr != nullptr)
{
dwarnln("pthread attr not supported");
return BAN::Error::from_errno(ENOTSUP);
}
LockGuard _(m_process_lock);
auto* new_thread = TRY(Thread::current().pthread_create(entry, arg));
MUST(m_threads.push_back(new_thread));
MUST(Processor::scheduler().add_thread(new_thread));
return new_thread->tid();
}
BAN::ErrorOr<long> Process::sys_pthread_exit(void* value)
{
LockGuard _(m_process_lock);
// main thread cannot call pthread_exit
if (&Thread::current() == m_threads.front())
return BAN::Error::from_errno(EINVAL);
TRY(m_exited_pthreads.emplace_back(&Thread::current(), value));
for (auto* thread : m_threads)
{
if (thread != &Thread::current())
continue;
m_process_lock.unlock();
thread->on_exit();
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<long> Process::sys_tcgetpgrp(int fd)
{
LockGuard _(m_process_lock);

View File

@ -117,8 +117,6 @@ namespace Kernel
true
));
thread->setup_exec();
thread_deleter.disable();
return thread;
@ -166,6 +164,21 @@ namespace Kernel
}
}
BAN::ErrorOr<Thread*> Thread::pthread_create(entry_t entry, void* arg)
{
auto* thread = TRY(create_userspace(m_process, m_process->page_table()));
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
thread->setup_exec_impl(
reinterpret_cast<uintptr_t>(entry),
reinterpret_cast<uintptr_t>(arg),
0, 0, 0
);
return thread;
}
BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip)
{
ASSERT(m_is_userspace);
@ -189,6 +202,8 @@ namespace Kernel
thread->m_interrupt_stack.sp = sp;
thread->m_interrupt_stack.ss = 0x10;
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
#if ARCH(x86_64)
thread->m_interrupt_registers.rax = 0;
#elif ARCH(i686)
@ -201,23 +216,34 @@ namespace Kernel
}
void Thread::setup_exec()
{
const auto& userspace_info = process().userspace_info();
ASSERT(userspace_info.entry);
setup_exec_impl(
userspace_info.entry,
userspace_info.argc,
reinterpret_cast<uintptr_t>(userspace_info.argv),
reinterpret_cast<uintptr_t>(userspace_info.envp),
userspace_info.file_fd
);
}
void Thread::setup_exec_impl(uintptr_t entry, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
{
ASSERT(is_userspace());
m_state = State::NotStarted;
// Signal mask is inherited
const auto& userspace_info = process().userspace_info();
ASSERT(userspace_info.entry);
// Initialize stack for returning
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(sp, userspace_info.entry);
write_to_stack(sp, userspace_info.file_fd);
write_to_stack(sp, userspace_info.envp);
write_to_stack(sp, userspace_info.argv);
write_to_stack(sp, userspace_info.argc);
write_to_stack(sp, entry);
write_to_stack(sp, arg3);
write_to_stack(sp, arg2);
write_to_stack(sp, arg1);
write_to_stack(sp, arg0);
});
m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);

View File

@ -17,6 +17,7 @@ set(LIBC_SOURCES
netdb.cpp
poll.cpp
printf_impl.cpp
pthread.cpp
pwd.cpp
scanf_impl.cpp
setjmp.cpp

View File

@ -90,6 +90,8 @@ __BEGIN_DECLS
O(SYS_FSYNC, fsync) \
O(SYS_SYMLINKAT, symlinkat) \
O(SYS_HARDLINKAT, hardlinkat) \
O(SYS_PTHREAD_CREATE, pthread_create) \
O(SYS_PTHREAD_EXIT, pthread_exit) \
enum Syscall
{

View File

@ -0,0 +1,49 @@
#include <BAN/Assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
struct pthread_trampoline_info_t
{
void* (*start_routine)(void*);
void* arg;
};
static void pthread_trampoline(void* arg)
{
pthread_trampoline_info_t info;
memcpy(&info, arg, sizeof(pthread_trampoline_info_t));
free(arg);
pthread_exit(info.start_routine(info.arg));
ASSERT_NOT_REACHED();
}
int pthread_create(pthread_t* __restrict thread, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg)
{
auto* info = static_cast<pthread_trampoline_info_t*>(malloc(sizeof(pthread_trampoline_info_t)));
if (info == nullptr)
return -1;
info->start_routine = start_routine;
info->arg = arg;
const auto ret = syscall(SYS_PTHREAD_CREATE, attr, pthread_trampoline, info);
if (ret == -1)
{
free(info);
return -1;
}
if (thread)
*thread = ret;
return 0;
}
void pthread_exit(void* value_ptr)
{
syscall(SYS_PTHREAD_EXIT, value_ptr);
ASSERT_NOT_REACHED();
}

View File

@ -0,0 +1,8 @@
set(SOURCES
main.cpp
)
add_executable(test-pthread ${SOURCES})
banan_link_library(test-pthread libc)
install(TARGETS test-pthread OPTIONAL)

View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* thread_func(void*)
{
printf("hello from thread\n");
return nullptr;
}
int main(int argc, char** argv)
{
pthread_t tid;
printf("creating thread\n");
if (pthread_create(&tid, nullptr, &thread_func, nullptr) == -1)
{
perror("pthread_create");
return 1;
}
sleep(1);
printf("exiting\n");
return 0;
}