summaryrefslogtreecommitdiff
path: root/lib/mlibc/options/posix/generic/pthread-stubs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mlibc/options/posix/generic/pthread-stubs.cpp')
-rw-r--r--lib/mlibc/options/posix/generic/pthread-stubs.cpp1426
1 files changed, 0 insertions, 1426 deletions
diff --git a/lib/mlibc/options/posix/generic/pthread-stubs.cpp b/lib/mlibc/options/posix/generic/pthread-stubs.cpp
deleted file mode 100644
index 9720bc2..0000000
--- a/lib/mlibc/options/posix/generic/pthread-stubs.cpp
+++ /dev/null
@@ -1,1426 +0,0 @@
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <bits/ensure.h>
-#include <frg/allocation.hpp>
-#include <frg/array.hpp>
-#include <mlibc/allocator.hpp>
-#include <mlibc/debug.hpp>
-#include <mlibc/posix-sysdeps.hpp>
-#include <mlibc/thread.hpp>
-#include <mlibc/tcb.hpp>
-#include <mlibc/tid.hpp>
-#include <mlibc/threads.hpp>
-
-static bool enableTrace = false;
-
-struct ScopeTrace {
- ScopeTrace(const char *file, int line, const char *function)
- : _file(file), _line(line), _function(function) {
- if(!enableTrace)
- return;
- mlibc::infoLogger() << "trace: Enter scope "
- << _file << ":" << _line << " (in function "
- << _function << ")" << frg::endlog;
- }
-
- ~ScopeTrace() {
- if(!enableTrace)
- return;
- mlibc::infoLogger() << "trace: Exit scope" << frg::endlog;
- }
-
-private:
- const char *_file;
- int _line;
- const char *_function;
-};
-
-#define SCOPE_TRACE() ScopeTrace(__FILE__, __LINE__, __FUNCTION__)
-
-static constexpr unsigned int mutexRecursive = 1;
-static constexpr unsigned int mutexErrorCheck = 2;
-
-// TODO: either use uint32_t or determine the bit based on sizeof(int).
-static constexpr unsigned int mutex_owner_mask = (static_cast<uint32_t>(1) << 30) - 1;
-static constexpr unsigned int mutex_waiters_bit = static_cast<uint32_t>(1) << 31;
-
-// Only valid for the internal __mlibc_m mutex of wrlocks.
-static constexpr unsigned int mutex_excl_bit = static_cast<uint32_t>(1) << 30;
-
-static constexpr unsigned int rc_count_mask = (static_cast<uint32_t>(1) << 31) - 1;
-static constexpr unsigned int rc_waiters_bit = static_cast<uint32_t>(1) << 31;
-
-static constexpr size_t default_stacksize = 0x200000;
-static constexpr size_t default_guardsize = 4096;
-
-// ----------------------------------------------------------------------------
-// pthread_attr and pthread functions.
-// ----------------------------------------------------------------------------
-
-// pthread_attr functions.
-int pthread_attr_init(pthread_attr_t *attr) {
- *attr = pthread_attr_t{};
- attr->__mlibc_stacksize = default_stacksize;
- attr->__mlibc_guardsize = default_guardsize;
- attr->__mlibc_detachstate = PTHREAD_CREATE_JOINABLE;
- return 0;
-}
-
-int pthread_attr_destroy(pthread_attr_t *) {
- return 0;
-}
-
-int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
- *detachstate = attr->__mlibc_detachstate;
- return 0;
-}
-int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
- if (detachstate != PTHREAD_CREATE_DETACHED &&
- detachstate != PTHREAD_CREATE_JOINABLE)
- return EINVAL;
-
- attr->__mlibc_detachstate = detachstate;
- return 0;
-}
-
-int pthread_attr_getstacksize(const pthread_attr_t *__restrict attr, size_t *__restrict stacksize) {
- *stacksize = attr->__mlibc_stacksize;
- return 0;
-}
-
-int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) {
- if (stacksize < PTHREAD_STACK_MIN)
- return EINVAL;
- attr->__mlibc_stacksize = stacksize;
- return 0;
-}
-
-int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) {
- *stackaddr = attr->__mlibc_stackaddr;
- return 0;
-}
-int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) {
- attr->__mlibc_stackaddr = stackaddr;
- return 0;
-}
-
-int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize) {
- *stackaddr = attr->__mlibc_stackaddr;
- *stacksize = attr->__mlibc_stacksize;
- return 0;
-}
-int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize) {
- if (stacksize < PTHREAD_STACK_MIN)
- return EINVAL;
- attr->__mlibc_stacksize = stacksize;
- attr->__mlibc_stackaddr = stackaddr;
- return 0;
-}
-
-int pthread_attr_getguardsize(const pthread_attr_t *__restrict attr, size_t *__restrict guardsize) {
- *guardsize = attr->__mlibc_guardsize;
- return 0;
-}
-int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
- attr->__mlibc_guardsize = guardsize;
- return 0;
-}
-
-int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) {
- *scope = attr->__mlibc_scope;
- return 0;
-}
-int pthread_attr_setscope(pthread_attr_t *attr, int scope) {
- if (scope != PTHREAD_SCOPE_SYSTEM &&
- scope != PTHREAD_SCOPE_PROCESS)
- return EINVAL;
- if (scope == PTHREAD_SCOPE_PROCESS)
- return ENOTSUP;
- attr->__mlibc_scope = scope;
- return 0;
-}
-
-int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched) {
- *inheritsched = attr->__mlibc_inheritsched;
- return 0;
-}
-int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) {
- if (inheritsched != PTHREAD_INHERIT_SCHED &&
- inheritsched != PTHREAD_EXPLICIT_SCHED)
- return EINVAL;
- attr->__mlibc_inheritsched = inheritsched;
- return 0;
-}
-
-int pthread_attr_getschedparam(const pthread_attr_t *__restrict attr, struct sched_param *__restrict schedparam) {
- *schedparam = attr->__mlibc_schedparam;
- return 0;
-}
-int pthread_attr_setschedparam(pthread_attr_t *__restrict attr, const struct sched_param *__restrict schedparam) {
- // TODO: this is supposed to return EINVAL for when the schedparam doesn't make sense
- // for the given schedpolicy.
- attr->__mlibc_schedparam = *schedparam;
- return 0;
-}
-
-int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict attr, int *__restrict policy) {
- *policy = attr->__mlibc_schedpolicy;
- return 0;
-}
-int pthread_attr_setschedpolicy(pthread_attr_t *__restrict attr, int policy) {
- if (policy != SCHED_FIFO && policy != SCHED_RR &&
- policy != SCHED_OTHER)
- return EINVAL;
- attr->__mlibc_schedpolicy = policy;
- return 0;
-}
-
-#if __MLIBC_LINUX_OPTION
-int pthread_attr_getaffinity_np(const pthread_attr_t *__restrict attr,
- size_t cpusetsize, cpu_set_t *__restrict cpusetp) {
- if (!attr)
- return EINVAL;
-
- if (!attr->__mlibc_cpuset) {
- memset(cpusetp, -1, cpusetsize);
- return 0;
- }
-
- for (size_t cnt = cpusetsize; cnt < attr->__mlibc_cpusetsize; cnt++)
- if (reinterpret_cast<char*>(attr->__mlibc_cpuset)[cnt] != '\0')
- return ERANGE;
-
- auto p = memcpy(cpusetp, attr->__mlibc_cpuset,
- std::min(cpusetsize, attr->__mlibc_cpusetsize));
- if (cpusetsize > attr->__mlibc_cpusetsize)
- memset(p, '\0', cpusetsize - attr->__mlibc_cpusetsize);
-
- return 0;
-}
-
-int pthread_attr_setaffinity_np(pthread_attr_t *__restrict attr,
- size_t cpusetsize, const cpu_set_t *__restrict cpusetp) {
- if (!attr)
- return EINVAL;
-
- if (!cpusetp || !cpusetsize) {
- attr->__mlibc_cpuset = NULL;
- attr->__mlibc_cpusetsize = 0;
- return 0;
- }
-
- if (attr->__mlibc_cpusetsize != cpusetsize) {
- auto newp = realloc(attr->__mlibc_cpuset, cpusetsize);
- if (!newp)
- return ENOMEM;
-
- attr->__mlibc_cpuset = static_cast<cpu_set_t*>(newp);
- attr->__mlibc_cpusetsize = cpusetsize;
- }
-
- memcpy(attr->__mlibc_cpuset, cpusetp, cpusetsize);
- return 0;
-}
-
-int pthread_attr_getsigmask_np(const pthread_attr_t *__restrict attr,
- sigset_t *__restrict sigmask) {
- if (!attr)
- return EINVAL;
-
- if (!attr->__mlibc_sigmaskset) {
- sigemptyset(sigmask);
- return PTHREAD_ATTR_NO_SIGMASK_NP;
- }
-
- *sigmask = attr->__mlibc_sigmask;
-
- return 0;
-}
-int pthread_attr_setsigmask_np(pthread_attr_t *__restrict attr,
- const sigset_t *__restrict sigmask) {
- if (!attr)
- return EINVAL;
-
- if (!sigmask) {
- attr->__mlibc_sigmaskset = 0;
- return 0;
- }
-
- attr->__mlibc_sigmask = *sigmask;
- attr->__mlibc_sigmaskset = 1;
-
- // Filter out internally used signals.
- sigdelset(&attr->__mlibc_sigmask, SIGCANCEL);
-
- return 0;
-}
-
-namespace {
- void get_own_stackinfo(void **stack_addr, size_t *stack_size) {
- auto fp = fopen("/proc/self/maps", "r");
- if (!fp) {
- mlibc::infoLogger() << "mlibc pthreads: /proc/self/maps does not exist! Producing incorrect"
- " stack results!" << frg::endlog;
- return;
- }
-
- char line[256];
- auto sp = mlibc::get_sp();
- while (fgets(line, 256, fp)) {
- uintptr_t from, to;
- if(sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &from, &to) != 2)
- continue;
- if (sp < to && sp > from) {
- // We need to return the lowest byte of the stack.
- *stack_addr = reinterpret_cast<void*>(from);
- *stack_size = to - from;
- fclose(fp);
- return;
- }
- }
-
- fclose(fp);
- }
-}
-
-int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
- auto tcb = reinterpret_cast<Tcb*>(thread);
- *attr = pthread_attr_t{};
-
- if (!tcb->stackAddr || !tcb->stackSize) {
- get_own_stackinfo(&attr->__mlibc_stackaddr, &attr->__mlibc_stacksize);
- } else {
- attr->__mlibc_stacksize = tcb->stackSize;
- attr->__mlibc_stackaddr = tcb->stackAddr;
- }
-
- attr->__mlibc_guardsize = tcb->guardSize;
- attr->__mlibc_detachstate = tcb->isJoinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED;
- mlibc::infoLogger() << "pthread_getattr_np(): Implementation is incomplete!" << frg::endlog;
- return 0;
-}
-
-int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *mask) {
- MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getthreadaffinity, ENOSYS);
- return mlibc::sys_getthreadaffinity(reinterpret_cast<Tcb*>(thread)->tid, cpusetsize, mask);
-}
-
-int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *mask) {
- MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setthreadaffinity, ENOSYS);
- return mlibc::sys_setthreadaffinity(reinterpret_cast<Tcb*>(thread)->tid, cpusetsize, mask);
-}
-#endif // __MLIBC_LINUX_OPTION
-
-extern "C" Tcb *__rtdl_allocateTcb();
-
-// pthread functions.
-int pthread_create(pthread_t *__restrict thread, const pthread_attr_t *__restrict attrp,
- void *(*entry) (void *), void *__restrict user_arg) {
- return mlibc::thread_create(thread, attrp, reinterpret_cast<void *>(entry), user_arg, false);
-}
-
-pthread_t pthread_self(void) {
- return reinterpret_cast<pthread_t>(mlibc::get_current_tcb());
-}
-
-int pthread_equal(pthread_t t1, pthread_t t2) {
- if(t1 == t2)
- return 1;
- return 0;
-}
-
-namespace {
- struct key_global_info {
- bool in_use;
-
- void (*dtor)(void *);
- uint64_t generation;
- };
-
- constinit frg::array<
- key_global_info,
- PTHREAD_KEYS_MAX
- > key_globals_{};
-
- FutexLock key_mutex_;
-}
-
-namespace mlibc {
- __attribute__ ((__noreturn__)) void do_exit() {
- sys_thread_exit();
- __builtin_unreachable();
- }
-}
-
-__attribute__ ((__noreturn__)) void pthread_exit(void *ret_val) {
- auto self = mlibc::get_current_tcb();
-
- if (__atomic_load_n(&self->cancelBits, __ATOMIC_RELAXED) & tcbExitingBit)
- mlibc::do_exit();
-
- __atomic_fetch_or(&self->cancelBits, tcbExitingBit, __ATOMIC_RELAXED);
-
- auto hand = self->cleanupEnd;
- while (hand) {
- auto old = hand;
- hand->func(hand->arg);
- hand = hand->prev;
- frg::destruct(getAllocator(), old);
- }
-
- for (size_t j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
- for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++) {
- if (auto v = pthread_getspecific(i)) {
- key_mutex_.lock();
- auto dtor = key_globals_[i].dtor;
- key_mutex_.unlock();
-
- if (dtor) {
- dtor(v);
- (*self->localKeys)[i].value = nullptr;
- }
- }
- }
- }
-
- self->returnValue.voidPtr = ret_val;
- __atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE);
- mlibc::sys_futex_wake(&self->didExit);
-
- // TODO: clean up thread resources when we are detached.
-
- // TODO: do exit(0) when we're the only thread instead
- mlibc::do_exit();
-}
-
-int pthread_join(pthread_t thread, void **ret) {
- return mlibc::thread_join(thread, ret);
-}
-
-int pthread_detach(pthread_t thread) {
- auto tcb = reinterpret_cast<Tcb*>(thread);
- if (!__atomic_load_n(&tcb->isJoinable, __ATOMIC_RELAXED))
- return EINVAL;
-
- int expected = 1;
- if(!__atomic_compare_exchange_n(&tcb->isJoinable, &expected, 0, false, __ATOMIC_RELEASE,
- __ATOMIC_RELAXED))
- return EINVAL;
-
- return 0;
-}
-
-void pthread_cleanup_push(void (*func) (void *), void *arg) {
- auto self = mlibc::get_current_tcb();
-
- auto hand = frg::construct<Tcb::CleanupHandler>(getAllocator());
- hand->func = func;
- hand->arg = arg;
- hand->next = nullptr;
- hand->prev = self->cleanupEnd;
-
- if (self->cleanupEnd)
- self->cleanupEnd->next = hand;
-
- self->cleanupEnd = hand;
-
- if (!self->cleanupBegin)
- self->cleanupBegin = self->cleanupEnd;
-}
-
-void pthread_cleanup_pop(int execute) {
- auto self = mlibc::get_current_tcb();
-
- auto hand = self->cleanupEnd;
-
- if (self->cleanupEnd)
- self->cleanupEnd = self->cleanupEnd->prev;
- if (self->cleanupEnd)
- self->cleanupEnd->next = nullptr;
-
- if (execute)
- hand->func(hand->arg);
-
- frg::destruct(getAllocator(), hand);
-}
-
-int pthread_setname_np(pthread_t thread, const char *name) {
- auto tcb = reinterpret_cast<Tcb*>(thread);
-
- auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_thread_setname, ENOSYS);
- if(int e = sysdep(tcb, name); e) {
- return e;
- }
-
- return 0;
-}
-
-int pthread_getname_np(pthread_t thread, char *name, size_t size) {
- auto tcb = reinterpret_cast<Tcb*>(thread);
-
- auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_thread_getname, ENOSYS);
- if(int e = sysdep(tcb, name, size); e) {
- return e;
- }
-
- return 0;
-}
-
-int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) {
- auto tcb = reinterpret_cast<Tcb*>(thread);
-
- MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setschedparam, ENOSYS);
- if(int e = mlibc::sys_setschedparam(tcb, policy, param); e) {
- return e;
- }
-
- return 0;
-}
-
-int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param) {
- auto tcb = reinterpret_cast<Tcb*>(thread);
-
- MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getschedparam, ENOSYS);
- if(int e = mlibc::sys_getschedparam(tcb, policy, param); e) {
- return e;
- }
-
- return 0;
-}
-
-//pthread cancel functions
-
-extern "C" void __mlibc_do_cancel() {
- //TODO(geert): for now the same as pthread_exit()
- pthread_exit(PTHREAD_CANCELED);
-}
-
-namespace {
-
- void sigcancel_handler(int signal, siginfo_t *info, void *ucontext) {
- ucontext_t *uctx = static_cast<ucontext_t*>(ucontext);
- // The function could be called from other signals, or from another
- // process, in which case we should do nothing.
- if (signal != SIGCANCEL || info->si_pid != getpid() ||
- info->si_code != SI_TKILL)
- return;
-
- auto tcb = reinterpret_cast<Tcb*>(mlibc::get_current_tcb());
- int old_value = tcb->cancelBits;
-
- /*
- * When a thread is marked with deferred cancellation and performs a blocking syscall,
- * the spec mandates that the syscall can get interrupted before it has caused any side
- * effects (e.g. before a read() has read any bytes from disk). If the syscall has
- * already caused side effects it should return its partial work, and set the program
- * counter just after the syscall. If the syscall hasn't caused any side effects, it
- * should fail with EINTR and set the program counter to the syscall instruction.
- *
- * cancellable_syscall:
- * test whether_a_cancel_is_queued
- * je cancel
- * syscall
- * end_cancellable_syscall
- *
- * The mlibc::sys_before_cancellable_syscall sysdep should return 1 when the
- * program counter is between the 'canellable_syscall' and 'end_cancellable_syscall' label.
- */
- if (!(old_value & tcbCancelAsyncBit) &&
- mlibc::sys_before_cancellable_syscall && !mlibc::sys_before_cancellable_syscall(uctx))
- return;
-
- int bitmask = tcbCancelTriggerBit | tcbCancelingBit;
- while (1) {
- int new_value = old_value | bitmask;
-
- // Check if we are already cancelled or exiting
- if (old_value == new_value || old_value & tcbExitingBit)
- return;
-
- int current_value = old_value;
- if (__atomic_compare_exchange_n(&tcb->cancelBits, &current_value,
- new_value, true,__ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
- tcb->returnValue.voidPtr = PTHREAD_CANCELED;
-
- // Perform cancellation
- __mlibc_do_cancel();
-
- break;
- }
-
- old_value = current_value;
- }
- }
-}
-
-namespace mlibc {
-namespace {
-
-struct PthreadSignalInstaller {
- PthreadSignalInstaller() {
- struct sigaction sa;
- sa.sa_sigaction = sigcancel_handler;
- sa.sa_flags = SA_SIGINFO;
- auto e = ENOSYS;
- if(sys_sigaction)
- e = sys_sigaction(SIGCANCEL, &sa, NULL);
- // Opt-out of cancellation support.
- if(e == ENOSYS)
- return;
- __ensure(!e);
- }
-};
-
-PthreadSignalInstaller pthread_signal_installer;
-
-} // anonymous namespace
-} // namespace mlibc
-
-int pthread_setcanceltype(int type, int *oldtype) {
- if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS)
- return EINVAL;
-
- auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb());
- int old_value = self->cancelBits;
- while (1) {
- int new_value = old_value & ~tcbCancelAsyncBit;
- if (type == PTHREAD_CANCEL_ASYNCHRONOUS)
- new_value |= tcbCancelAsyncBit;
-
- if (oldtype)
- *oldtype = ((old_value & tcbCancelAsyncBit)
- ? PTHREAD_CANCEL_ASYNCHRONOUS
- : PTHREAD_CANCEL_DEFERRED);
-
- // Avoid unecessary atomic op.
- if (old_value == new_value)
- break;
-
- int current_value = old_value;
- if (__atomic_compare_exchange_n(&self->cancelBits, &current_value,
- new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
-
- if (mlibc::tcb_async_cancelled(new_value))
- __mlibc_do_cancel();
-
- break;
- }
-
- old_value = current_value;
- }
-
- return 0;
-}
-int pthread_setcancelstate(int state, int *oldstate) {
- if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE)
- return EINVAL;
-
- auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb());
- int old_value = self->cancelBits;
- while (1) {
- int new_value = old_value & ~tcbCancelEnableBit;
- if (state == PTHREAD_CANCEL_ENABLE)
- new_value |= tcbCancelEnableBit;
-
- if (oldstate)
- *oldstate = ((old_value & tcbCancelEnableBit)
- ? PTHREAD_CANCEL_ENABLE
- : PTHREAD_CANCEL_DISABLE);
-
- // Avoid unecessary atomic op.
- if (old_value == new_value)
- break;
-
- int current_value = old_value;
- if (__atomic_compare_exchange_n(&self->cancelBits, &current_value,
- new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
-
- if (mlibc::tcb_async_cancelled(new_value))
- __mlibc_do_cancel();
-
- sigset_t set = {};
- sigaddset(&set, SIGCANCEL);
- if (new_value & PTHREAD_CANCEL_ENABLE)
- sigprocmask(SIG_UNBLOCK, &set, NULL);
- else
- sigprocmask(SIG_BLOCK, &set, NULL);
- break;
- }
-
- old_value = current_value;
- }
-
- return 0;
-}
-void pthread_testcancel(void) {
- auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb());
- int value = self->cancelBits;
- if ((value & tcbCancelEnableBit) && (value & tcbCancelTriggerBit)) {
- __mlibc_do_cancel();
- __builtin_unreachable();
- }
-}
-int pthread_cancel(pthread_t thread) {
- if (!mlibc::sys_tgkill) {
- MLIBC_MISSING_SYSDEP();
- return ENOSYS;
- }
-
- auto tcb = reinterpret_cast<Tcb *>(thread);
- // Check if the TCB is valid, somewhat..
- if (tcb->selfPointer != tcb)
- return ESRCH;
-
- int old_value = __atomic_load_n(&tcb->cancelBits, __ATOMIC_RELAXED);
- while (1) {
- int bitmask = tcbCancelTriggerBit;
-
- int new_value = old_value | bitmask;
- if (old_value == new_value)
- break;
-
- int current_value = old_value;
- if (__atomic_compare_exchange_n(&tcb->cancelBits, &current_value,
- new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
- if (mlibc::tcb_cancel_enabled(new_value)) {
- pid_t pid = getpid();
-
- int res = mlibc::sys_tgkill(pid, tcb->tid, SIGCANCEL);
-
- current_value = __atomic_load_n(&tcb->cancelBits, __ATOMIC_RELAXED);
-
- // If we can't find the thread anymore, it's possible that it exited between
- // us setting the cancel trigger bit, and us sending the signal. Check the
- // cancelBits for tcbExitingBit to confirm that.
- // XXX(qookie): This will be an use-after-free once we start freeing TCBs on
- // exit. Perhaps the TCB should be refcounted.
- if (!(res == ESRCH && (current_value & tcbExitingBit)))
- return res;
- }
-
- break;
- }
-
- old_value = current_value;
- }
-
- return 0;
-}
-
-int pthread_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void)) {
- auto self = mlibc::get_current_tcb();
-
- auto hand = frg::construct<Tcb::AtforkHandler>(getAllocator());
- if (!hand)
- return -1;
-
- hand->prepare = prepare;
- hand->parent = parent;
- hand->child = child;
- hand->next = nullptr;
- hand->prev = self->atforkEnd;
-
- if (self->atforkEnd)
- self->atforkEnd->next = hand;
-
- self->atforkEnd = hand;
-
- if (!self->atforkBegin)
- self->atforkBegin = self->atforkEnd;
-
- return 0;
-}
-
-// ----------------------------------------------------------------------------
-// pthread_key functions.
-// ----------------------------------------------------------------------------
-
-int pthread_key_create(pthread_key_t *out, void (*destructor)(void *)) {
- SCOPE_TRACE();
-
- auto g = frg::guard(&key_mutex_);
-
- pthread_key_t key = PTHREAD_KEYS_MAX;
- for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++) {
- if (!key_globals_[i].in_use) {
- key = i;
- break;
- }
- }
-
- if (key == PTHREAD_KEYS_MAX)
- return EAGAIN;
-
- key_globals_[key].in_use = true;
- key_globals_[key].dtor = destructor;
-
- *out = key;
-
- return 0;
-}
-
-int pthread_key_delete(pthread_key_t key) {
- SCOPE_TRACE();
-
- auto g = frg::guard(&key_mutex_);
-
- if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use)
- return EINVAL;
-
- key_globals_[key].in_use = false;
- key_globals_[key].dtor = nullptr;
- key_globals_[key].generation++;
-
- return 0;
-}
-
-void *pthread_getspecific(pthread_key_t key) {
- SCOPE_TRACE();
-
- auto self = mlibc::get_current_tcb();
- auto g = frg::guard(&key_mutex_);
-
- if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use)
- return nullptr;
-
- if (key_globals_[key].generation > (*self->localKeys)[key].generation) {
- (*self->localKeys)[key].value = nullptr;
- (*self->localKeys)[key].generation = key_globals_[key].generation;
- }
-
- return (*self->localKeys)[key].value;
-}
-
-int pthread_setspecific(pthread_key_t key, const void *value) {
- SCOPE_TRACE();
-
- auto self = mlibc::get_current_tcb();
- auto g = frg::guard(&key_mutex_);
-
- if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use)
- return EINVAL;
-
- (*self->localKeys)[key].value = const_cast<void *>(value);
- (*self->localKeys)[key].generation = key_globals_[key].generation;
-
- return 0;
-}
-
-// ----------------------------------------------------------------------------
-// pthread_once functions.
-// ----------------------------------------------------------------------------
-
-static constexpr unsigned int onceComplete = 1;
-static constexpr unsigned int onceLocked = 2;
-
-int pthread_once(pthread_once_t *once, void (*function) (void)) {
- SCOPE_TRACE();
-
- auto expected = __atomic_load_n(&once->__mlibc_done, __ATOMIC_ACQUIRE);
-
- // fast path: the function was already run.
- while(!(expected & onceComplete)) {
- if(!expected) {
- // try to acquire the mutex.
- if(!__atomic_compare_exchange_n(&once->__mlibc_done,
- &expected, onceLocked, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE))
- continue;
-
- function();
-
- // unlock the mutex.
- __atomic_exchange_n(&once->__mlibc_done, onceComplete, __ATOMIC_RELEASE);
- if(int e = mlibc::sys_futex_wake((int *)&once->__mlibc_done); e)
- __ensure(!"sys_futex_wake() failed");
- return 0;
- }else{
- // a different thread is currently running the initializer.
- __ensure(expected == onceLocked);
- if(int e = mlibc::sys_futex_wait((int *)&once->__mlibc_done, onceLocked, nullptr); e)
- __ensure(!"sys_futex_wait() failed");
- expected = __atomic_load_n(&once->__mlibc_done, __ATOMIC_ACQUIRE);
- }
- }
-
- return 0;
-}
-
-// ----------------------------------------------------------------------------
-// pthread_mutexattr and pthread_mutex functions.
-// ----------------------------------------------------------------------------
-
-// pthread_mutexattr functions
-int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
- SCOPE_TRACE();
- return mlibc::thread_mutexattr_init(attr);
-}
-
-int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
- SCOPE_TRACE();
- return mlibc::thread_mutexattr_destroy(attr);
-}
-
-int pthread_mutexattr_gettype(const pthread_mutexattr_t *__restrict attr, int *__restrict type) {
- return mlibc::thread_mutexattr_gettype(attr, type);
-}
-
-int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
- return mlibc::thread_mutexattr_settype(attr, type);
-}
-
-int pthread_mutexattr_getrobust(const pthread_mutexattr_t *__restrict attr,
- int *__restrict robust) {
- *robust = attr->__mlibc_robust;
- return 0;
-}
-int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr, int robust) {
- if (robust != PTHREAD_MUTEX_STALLED && robust != PTHREAD_MUTEX_ROBUST)
- return EINVAL;
-
- attr->__mlibc_robust = robust;
- return 0;
-}
-
-int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared) {
- *pshared = attr->__mlibc_pshared;
- return 0;
-}
-int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
- if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
- return EINVAL;
-
- attr->__mlibc_pshared = pshared;
- return 0;
-}
-
-int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *__restrict attr,
- int *__restrict protocol) {
- *protocol = attr->__mlibc_protocol;
- return 0;
-}
-
-int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol) {
- if (protocol != PTHREAD_PRIO_NONE && protocol != PTHREAD_PRIO_INHERIT
- && protocol != PTHREAD_PRIO_PROTECT)
- return EINVAL;
-
- attr->__mlibc_protocol = protocol;
- return 0;
-}
-
-int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *__restrict attr,
- int *__restrict prioceiling) {
- (void)attr;
- (void)prioceiling;
- return EINVAL;
-}
-
-int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling) {
- (void)attr;
- (void)prioceiling;
- return EINVAL;
-}
-
-// pthread_mutex functions
-int pthread_mutex_init(pthread_mutex_t *__restrict mutex,
- const pthread_mutexattr_t *__restrict attr) {
- SCOPE_TRACE();
-
- return mlibc::thread_mutex_init(mutex, attr);
-}
-
-int pthread_mutex_destroy(pthread_mutex_t *mutex) {
- return mlibc::thread_mutex_destroy(mutex);
-}
-
-int pthread_mutex_lock(pthread_mutex_t *mutex) {
- SCOPE_TRACE();
-
- return mlibc::thread_mutex_lock(mutex);
-}
-
-int pthread_mutex_trylock(pthread_mutex_t *mutex) {
- SCOPE_TRACE();
-
- unsigned int this_tid = mlibc::this_tid();
- unsigned int expected = __atomic_load_n(&mutex->__mlibc_state, __ATOMIC_RELAXED);
- if(!expected) {
- // Try to take the mutex here.
- if(__atomic_compare_exchange_n(&mutex->__mlibc_state,
- &expected, this_tid, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
- __ensure(!mutex->__mlibc_recursion);
- mutex->__mlibc_recursion = 1;
- return 0;
- }
- } else {
- // If this (recursive) mutex is already owned by us, increment the recursion level.
- if((expected & mutex_owner_mask) == this_tid) {
- if(!(mutex->__mlibc_flags & mutexRecursive)) {
- return EBUSY;
- }
- ++mutex->__mlibc_recursion;
- return 0;
- }
- }
-
- return EBUSY;
-}
-
-int pthread_mutex_timedlock(pthread_mutex_t *__restrict,
- const struct timespec *__restrict) {
- __ensure(!"Not implemented");
- __builtin_unreachable();
-}
-
-int pthread_mutex_unlock(pthread_mutex_t *mutex) {
- SCOPE_TRACE();
-
- return mlibc::thread_mutex_unlock(mutex);
-}
-
-int pthread_mutex_consistent(pthread_mutex_t *) {
- __ensure(!"Not implemented");
- __builtin_unreachable();
-}
-
-// ----------------------------------------------------------------------------
-// pthread_condattr and pthread_cond functions.
-// ----------------------------------------------------------------------------
-
-int pthread_condattr_init(pthread_condattr_t *attr) {
- attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE;
- attr->__mlibc_clock = CLOCK_REALTIME;
- return 0;
-}
-
-int pthread_condattr_destroy(pthread_condattr_t *attr) {
- memset(attr, 0, sizeof(*attr));
- return 0;
-}
-
-int pthread_condattr_getclock(const pthread_condattr_t *__restrict attr,
- clockid_t *__restrict clock) {
- *clock = attr->__mlibc_clock;
- return 0;
-}
-
-int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock) {
- if (clock != CLOCK_REALTIME && clock != CLOCK_MONOTONIC
- && clock != CLOCK_MONOTONIC_RAW && clock != CLOCK_REALTIME_COARSE
- && clock != CLOCK_MONOTONIC_COARSE && clock != CLOCK_BOOTTIME)
- return EINVAL;
-
- attr->__mlibc_clock = clock;
- return 0;
-}
-
-int pthread_condattr_getpshared(const pthread_condattr_t *__restrict attr,
- int *__restrict pshared) {
- *pshared = attr->__mlibc_pshared;
- return 0;
-}
-
-int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
- if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
- return EINVAL;
-
- attr->__mlibc_pshared = pshared;
- return 0;
-}
-
-int pthread_cond_init(pthread_cond_t *__restrict cond, const pthread_condattr_t *__restrict attr) {
- SCOPE_TRACE();
-
- return mlibc::thread_cond_init(cond, attr);
-}
-
-int pthread_cond_destroy(pthread_cond_t *cond) {
- SCOPE_TRACE();
-
- return mlibc::thread_cond_destroy(cond);
-}
-
-int pthread_cond_wait(pthread_cond_t *__restrict cond, pthread_mutex_t *__restrict mutex) {
- return pthread_cond_timedwait(cond, mutex, nullptr);
-}
-
-int pthread_cond_timedwait(pthread_cond_t *__restrict cond, pthread_mutex_t *__restrict mutex,
- const struct timespec *__restrict abstime) {
- return mlibc::thread_cond_timedwait(cond, mutex, abstime);
-}
-
-int pthread_cond_signal(pthread_cond_t *cond) {
- SCOPE_TRACE();
-
- return pthread_cond_broadcast(cond);
-}
-
-int pthread_cond_broadcast(pthread_cond_t *cond) {
- SCOPE_TRACE();
-
- return mlibc::thread_cond_broadcast(cond);
-}
-
-// ----------------------------------------------------------------------------
-// pthread_barrierattr and pthread_barrier functions.
-// ----------------------------------------------------------------------------
-
-int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
- attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE;
- return 0;
-}
-
-int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict attr,
- int *__restrict pshared) {
- *pshared = attr->__mlibc_pshared;
- return 0;
-}
-
-int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) {
- if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE)
- return EINVAL;
-
- attr->__mlibc_pshared = pshared;
- return 0;
-}
-
-int pthread_barrierattr_destroy(pthread_barrierattr_t *) {
- return 0;
-}
-
-int pthread_barrier_init(pthread_barrier_t *__restrict barrier,
- const pthread_barrierattr_t *__restrict attr, unsigned count) {
- if (count == 0)
- return EINVAL;
-
- barrier->__mlibc_waiting = 0;
- barrier->__mlibc_inside = 0;
- barrier->__mlibc_seq = 0;
- barrier->__mlibc_count = count;
-
- // Since we don't implement these yet, set a flag to error later.
- auto pshared = attr ? attr->__mlibc_pshared : PTHREAD_PROCESS_PRIVATE;
- barrier->__mlibc_flags = pshared;
-
- return 0;
-}
-
-int pthread_barrier_destroy(pthread_barrier_t *barrier) {
- // Wait until there are no threads still using the barrier.
- unsigned inside = 0;
- do {
- unsigned expected = __atomic_load_n(&barrier->__mlibc_inside, __ATOMIC_RELAXED);
- if (expected == 0)
- break;
-
- int e = mlibc::sys_futex_wait((int *)&barrier->__mlibc_inside, expected, nullptr);
- if (e != 0 && e != EAGAIN && e != EINTR)
- mlibc::panicLogger() << "mlibc: sys_futex_wait() returned error " << e << frg::endlog;
- } while (inside > 0);
-
- memset(barrier, 0, sizeof *barrier);
- return 0;
-}
-
-int pthread_barrier_wait(pthread_barrier_t *barrier) {
- if (barrier->__mlibc_flags != 0) {
- mlibc::panicLogger() << "mlibc: pthread_barrier_t flags were non-zero"
- << frg::endlog;
- }
-
- // inside is incremented on entry and decremented on exit.
- // This is used to synchronise with pthread_barrier_destroy, to ensure that a thread doesn't pass
- // the barrier and immediately destroy its state while other threads still rely on it.
-
- __atomic_fetch_add(&barrier->__mlibc_inside, 1, __ATOMIC_ACQUIRE);
-
- auto leave = [&](){
- unsigned inside = __atomic_sub_fetch(&barrier->__mlibc_inside, 1, __ATOMIC_RELEASE);
- if (inside == 0)
- mlibc::sys_futex_wake((int *)&barrier->__mlibc_inside);
- };
-
- unsigned seq = __atomic_load_n(&barrier->__mlibc_seq, __ATOMIC_ACQUIRE);
-
- while (true) {
- unsigned expected = __atomic_load_n(&barrier->__mlibc_waiting, __ATOMIC_RELAXED);
- bool swapped = __atomic_compare_exchange_n(&barrier->__mlibc_waiting, &expected, expected + 1, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
-
- if (swapped) {
- if (expected + 1 == barrier->__mlibc_count) {
- // We were the last thread to hit the barrier. Reset waiters and wake the others.
- __atomic_fetch_add(&barrier->__mlibc_seq, 1, __ATOMIC_ACQUIRE);
- __atomic_store_n(&barrier->__mlibc_waiting, 0, __ATOMIC_RELEASE);
-
- mlibc::sys_futex_wake((int *)&barrier->__mlibc_seq);
-
- leave();
- return PTHREAD_BARRIER_SERIAL_THREAD;
- }
-
- while (true) {
- int e = mlibc::sys_futex_wait((int *)&barrier->__mlibc_seq, seq, nullptr);
- if (e != 0 && e != EAGAIN && e != EINTR)
- mlibc::panicLogger() << "mlibc: sys_futex_wait() returned error " << e << frg::endlog;
-
- unsigned newSeq = __atomic_load_n(&barrier->__mlibc_seq, __ATOMIC_ACQUIRE);
- if (newSeq > seq) {
- leave();
- return 0;
- }
- }
- }
- }
-}
-
-// ----------------------------------------------------------------------------
-// pthread_rwlock functions.
-// ----------------------------------------------------------------------------
-
-namespace {
- void rwlock_m_lock(pthread_rwlock_t *rw, bool excl) {
- unsigned int m_expected = __atomic_load_n(&rw->__mlibc_m, __ATOMIC_RELAXED);
- while(true) {
- if(m_expected) {
- __ensure(m_expected & mutex_owner_mask);
-
- // Try to set the waiters bit.
- if(!(m_expected & mutex_waiters_bit)) {
- unsigned int desired = m_expected | mutex_waiters_bit;
- if(!__atomic_compare_exchange_n(&rw->__mlibc_m,
- &m_expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
- continue;
- }
-
- // Wait on the futex.
- mlibc::sys_futex_wait((int *)&rw->__mlibc_m, m_expected | mutex_waiters_bit, nullptr);
-
- // Opportunistically try to take the lock after we wake up.
- m_expected = 0;
- }else{
- // Try to lock the mutex.
- unsigned int desired = 1;
- if(excl)
- desired |= mutex_excl_bit;
- if(__atomic_compare_exchange_n(&rw->__mlibc_m,
- &m_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
- break;
- }
- }
- }
-
- int rwlock_m_trylock(pthread_rwlock_t *rw, bool excl) {
- unsigned int m_expected = __atomic_load_n(&rw->__mlibc_m, __ATOMIC_RELAXED);
- if(!m_expected) {
- // Try to lock the mutex.
- unsigned int desired = 1;
- if(excl)
- desired |= mutex_excl_bit;
- if(__atomic_compare_exchange_n(&rw->__mlibc_m,
- &m_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
- return 0;
- }
-
- __ensure(m_expected & mutex_owner_mask);
-
- // POSIX says that this function should never block but also that
- // readers should not be blocked by readers. We implement this by returning EAGAIN
- // (and not EBUSY) if a reader would block a reader.
- if(!excl && !(m_expected & mutex_excl_bit))
- return EAGAIN;
-
- return EBUSY;
- }
-
- void rwlock_m_unlock(pthread_rwlock_t *rw) {
- auto m = __atomic_exchange_n(&rw->__mlibc_m, 0, __ATOMIC_RELEASE);
- if(m & mutex_waiters_bit)
- mlibc::sys_futex_wake((int *)&rw->__mlibc_m);
- }
-}
-
-int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
- attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE;
- return 0;
-}
-
-int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *__restrict attr,
- int *__restrict pshared) {
- *pshared = attr->__mlibc_pshared;
- return 0;
-}
-
-int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
- if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE)
- return EINVAL;
-
- attr->__mlibc_pshared = pshared;
- return 0;
-}
-
-int pthread_rwlockattr_destroy(pthread_rwlockattr_t *) {
- return 0;
-}
-
-int pthread_rwlock_init(pthread_rwlock_t *__restrict rw, const pthread_rwlockattr_t *__restrict attr) {
- SCOPE_TRACE();
- rw->__mlibc_m = 0;
- rw->__mlibc_rc = 0;
-
- // Since we don't implement this yet, set a flag to error later.
- auto pshared = attr ? attr->__mlibc_pshared : PTHREAD_PROCESS_PRIVATE;
- rw->__mlibc_flags = pshared;
- return 0;
-}
-
-int pthread_rwlock_destroy(pthread_rwlock_t *rw) {
- __ensure(!rw->__mlibc_m);
- __ensure(!rw->__mlibc_rc);
- return 0;
-}
-
-int pthread_rwlock_trywrlock(pthread_rwlock_t *rw) {
- SCOPE_TRACE();
-
- if (rw->__mlibc_flags != 0) {
- mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
- << frg::endlog;
- }
-
- // Take the __mlibc_m mutex.
- // Will be released in pthread_rwlock_unlock().
- if(int e = rwlock_m_trylock(rw, true))
- return e;
-
- // Check that there are no readers.
- unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE);
- if(rc_expected) {
- rwlock_m_unlock(rw);
- return EBUSY;
- }
-
- return 0;
-}
-
-int pthread_rwlock_wrlock(pthread_rwlock_t *rw) {
- SCOPE_TRACE();
-
- if (rw->__mlibc_flags != 0) {
- mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
- << frg::endlog;
- }
-
- // Take the __mlibc_m mutex.
- // Will be released in pthread_rwlock_unlock().
- rwlock_m_lock(rw, true);
-
- // Now wait until there are no more readers.
- unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE);
- while(true) {
- if(!rc_expected)
- break;
-
- __ensure(rc_expected & rc_count_mask);
-
- // Try to set the waiters bit.
- if(!(rc_expected & rc_waiters_bit)) {
- unsigned int desired = rc_expected | rc_count_mask;
- if(!__atomic_compare_exchange_n(&rw->__mlibc_rc,
- &rc_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE))
- continue;
- }
-
- // Wait on the futex.
- mlibc::sys_futex_wait((int *)&rw->__mlibc_rc, rc_expected | rc_waiters_bit, nullptr);
-
- // Re-check the reader counter.
- rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE);
- }
-
- return 0;
-}
-
-int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) {
- SCOPE_TRACE();
-
- if (rw->__mlibc_flags != 0) {
- mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
- << frg::endlog;
- }
-
- // Increment the reader count while holding the __mlibc_m mutex.
- if(int e = rwlock_m_trylock(rw, false); e)
- return e;
- __atomic_fetch_add(&rw->__mlibc_rc, 1, __ATOMIC_ACQUIRE);
- rwlock_m_unlock(rw);
-
- return 0;
-}
-
-int pthread_rwlock_rdlock(pthread_rwlock_t *rw) {
- SCOPE_TRACE();
-
- if (rw->__mlibc_flags != 0) {
- mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
- << frg::endlog;
- }
-
- // Increment the reader count while holding the __mlibc_m mutex.
- rwlock_m_lock(rw, false);
- __atomic_fetch_add(&rw->__mlibc_rc, 1, __ATOMIC_ACQUIRE);
- rwlock_m_unlock(rw);
-
- return 0;
-}
-
-int pthread_rwlock_unlock(pthread_rwlock_t *rw) {
- SCOPE_TRACE();
-
- unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_RELAXED);
- if(!rc_expected) {
- // We are doing a write-unlock.
- rwlock_m_unlock(rw);
- return 0;
- }else{
- // We are doing a read-unlock.
- while(true) {
- unsigned int count = rc_expected & rc_count_mask;
- __ensure(count);
-
- // Try to decrement the count.
- if(count == 1 && (rc_expected & rc_waiters_bit)) {
- unsigned int desired = 0;
- if(!__atomic_compare_exchange_n(&rw->__mlibc_rc,
- &rc_expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED))
- continue;
-
- // Wake the futex.
- mlibc::sys_futex_wake((int *)&rw->__mlibc_rc);
- break;
- }else{
- unsigned int desired = (rc_expected & ~rc_count_mask) | (count - 1);
- if(!__atomic_compare_exchange_n(&rw->__mlibc_rc,
- &rc_expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED))
- continue;
- break;
- }
- }
-
- return 0;
- }
-}
-
-int pthread_getcpuclockid(pthread_t, clockid_t *) {
- mlibc::infoLogger() << "mlibc: pthread_getcpuclockid() always returns ENOENT"
- << frg::endlog;
- return ENOENT;
-}