Kernel/LibC: Add basic support for pthread_{create,exit}
This commit is contained in:
parent
788f5429e1
commit
c1618e2b5d
|
@ -182,6 +182,9 @@ namespace Kernel
|
||||||
BAN::ErrorOr<long> sys_sigpending(sigset_t* set);
|
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_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_tcgetpgrp(int fd);
|
||||||
BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid);
|
BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid);
|
||||||
|
|
||||||
|
@ -290,6 +293,13 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::Vector<Thread*> m_threads;
|
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_interval_ns { 0 };
|
||||||
uint64_t m_alarm_wake_time_ns { 0 };
|
uint64_t m_alarm_wake_time_ns { 0 };
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ namespace Kernel
|
||||||
static BAN::ErrorOr<Thread*> create_userspace(Process*, PageTable&);
|
static BAN::ErrorOr<Thread*> create_userspace(Process*, PageTable&);
|
||||||
~Thread();
|
~Thread();
|
||||||
|
|
||||||
|
BAN::ErrorOr<Thread*> pthread_create(entry_t, void*);
|
||||||
|
|
||||||
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip);
|
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip);
|
||||||
void setup_exec();
|
void setup_exec();
|
||||||
void setup_process_cleanup();
|
void setup_process_cleanup();
|
||||||
|
|
|
@ -185,6 +185,8 @@ namespace Kernel
|
||||||
process->m_userspace_info.envp = nullptr;
|
process->m_userspace_info.envp = nullptr;
|
||||||
|
|
||||||
auto* thread = MUST(Thread::create_userspace(process, process->page_table()));
|
auto* thread = MUST(Thread::create_userspace(process, process->page_table()));
|
||||||
|
thread->setup_exec();
|
||||||
|
|
||||||
process->add_thread(thread);
|
process->add_thread(thread);
|
||||||
process->register_to_scheduler();
|
process->register_to_scheduler();
|
||||||
return process;
|
return process;
|
||||||
|
@ -208,6 +210,7 @@ namespace Kernel
|
||||||
Process::~Process()
|
Process::~Process()
|
||||||
{
|
{
|
||||||
ASSERT(m_threads.empty());
|
ASSERT(m_threads.empty());
|
||||||
|
ASSERT(m_exited_pthreads.empty());
|
||||||
ASSERT(m_mapped_regions.empty());
|
ASSERT(m_mapped_regions.empty());
|
||||||
ASSERT(!m_page_table);
|
ASSERT(!m_page_table);
|
||||||
}
|
}
|
||||||
|
@ -238,6 +241,8 @@ namespace Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_exited_pthreads.clear();
|
||||||
|
|
||||||
ProcFileSystem::get().on_process_delete(*this);
|
ProcFileSystem::get().on_process_delete(*this);
|
||||||
|
|
||||||
m_process_lock.lock();
|
m_process_lock.lock();
|
||||||
|
@ -2079,6 +2084,43 @@ namespace Kernel
|
||||||
return 0;
|
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)
|
BAN::ErrorOr<long> Process::sys_tcgetpgrp(int fd)
|
||||||
{
|
{
|
||||||
LockGuard _(m_process_lock);
|
LockGuard _(m_process_lock);
|
||||||
|
|
|
@ -117,8 +117,6 @@ namespace Kernel
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
|
|
||||||
thread->setup_exec();
|
|
||||||
|
|
||||||
thread_deleter.disable();
|
thread_deleter.disable();
|
||||||
|
|
||||||
return thread;
|
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)
|
BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip)
|
||||||
{
|
{
|
||||||
ASSERT(m_is_userspace);
|
ASSERT(m_is_userspace);
|
||||||
|
@ -189,6 +202,8 @@ namespace Kernel
|
||||||
thread->m_interrupt_stack.sp = sp;
|
thread->m_interrupt_stack.sp = sp;
|
||||||
thread->m_interrupt_stack.ss = 0x10;
|
thread->m_interrupt_stack.ss = 0x10;
|
||||||
|
|
||||||
|
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
|
||||||
|
|
||||||
#if ARCH(x86_64)
|
#if ARCH(x86_64)
|
||||||
thread->m_interrupt_registers.rax = 0;
|
thread->m_interrupt_registers.rax = 0;
|
||||||
#elif ARCH(i686)
|
#elif ARCH(i686)
|
||||||
|
@ -201,23 +216,34 @@ namespace Kernel
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::setup_exec()
|
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());
|
ASSERT(is_userspace());
|
||||||
m_state = State::NotStarted;
|
m_state = State::NotStarted;
|
||||||
|
|
||||||
// Signal mask is inherited
|
// Signal mask is inherited
|
||||||
|
|
||||||
const auto& userspace_info = process().userspace_info();
|
|
||||||
ASSERT(userspace_info.entry);
|
|
||||||
|
|
||||||
// Initialize stack for returning
|
// Initialize stack for returning
|
||||||
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
|
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
|
||||||
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
|
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
|
||||||
write_to_stack(sp, userspace_info.entry);
|
write_to_stack(sp, entry);
|
||||||
write_to_stack(sp, userspace_info.file_fd);
|
write_to_stack(sp, arg3);
|
||||||
write_to_stack(sp, userspace_info.envp);
|
write_to_stack(sp, arg2);
|
||||||
write_to_stack(sp, userspace_info.argv);
|
write_to_stack(sp, arg1);
|
||||||
write_to_stack(sp, userspace_info.argc);
|
write_to_stack(sp, arg0);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);
|
m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);
|
||||||
|
|
|
@ -17,6 +17,7 @@ set(LIBC_SOURCES
|
||||||
netdb.cpp
|
netdb.cpp
|
||||||
poll.cpp
|
poll.cpp
|
||||||
printf_impl.cpp
|
printf_impl.cpp
|
||||||
|
pthread.cpp
|
||||||
pwd.cpp
|
pwd.cpp
|
||||||
scanf_impl.cpp
|
scanf_impl.cpp
|
||||||
setjmp.cpp
|
setjmp.cpp
|
||||||
|
|
|
@ -90,6 +90,8 @@ __BEGIN_DECLS
|
||||||
O(SYS_FSYNC, fsync) \
|
O(SYS_FSYNC, fsync) \
|
||||||
O(SYS_SYMLINKAT, symlinkat) \
|
O(SYS_SYMLINKAT, symlinkat) \
|
||||||
O(SYS_HARDLINKAT, hardlinkat) \
|
O(SYS_HARDLINKAT, hardlinkat) \
|
||||||
|
O(SYS_PTHREAD_CREATE, pthread_create) \
|
||||||
|
O(SYS_PTHREAD_EXIT, pthread_exit) \
|
||||||
|
|
||||||
enum Syscall
|
enum Syscall
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
set(SOURCES
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(test-pthread ${SOURCES})
|
||||||
|
banan_link_library(test-pthread libc)
|
||||||
|
|
||||||
|
install(TARGETS test-pthread OPTIONAL)
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue