Make improvements

- Improve compatibility with Blink virtual machine
- Add non-POSIX APIs for joining threads and signal masks
- Never ever use anything except 32-bit integers for atomics
- Add some `#undef` statements to workaround `ctags` problems
This commit is contained in:
Justine Tunney 2022-11-10 21:52:47 -08:00
parent b46ac13504
commit f2af97711b
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
114 changed files with 902 additions and 363 deletions

View file

@ -78,7 +78,6 @@ struct PosixThread {
nsync_dll_element_ list; // list of threads
jmp_buf exiter; // for pthread_exit
pthread_attr_t attr;
sigset_t sigmask;
struct _pthread_cleanup_buffer *cleanup;
};

View file

@ -0,0 +1,37 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/str/str.h"
#include "libc/thread/thread2.h"
/**
* Gets signal mask on thread attributes object.
*
* @param attr is the thread attributes object
* @param sigmask will receive the output signal mask on success, or
* null if a simple presence check is desired
* @return 0 on success, errno on error, or `PTHREAD_ATTR_NO_SIGMASK_NP`
* if there wasn't any signal mask present in `attr`
*/
errno_t pthread_attr_getsigmask_np(const pthread_attr_t *attr,
sigset_t *sigmask) {
_Static_assert(sizeof(attr->__sigmask) == sizeof(*sigmask), "");
if (!attr->__havesigmask) return PTHREAD_ATTR_NO_SIGMASK_NP;
if (sigmask) memcpy(sigmask, attr->__sigmask, sizeof(*sigmask));
return 0;
}

View file

@ -42,7 +42,6 @@
*/
errno_t pthread_attr_setschedparam(pthread_attr_t *attr,
const struct sched_param *param) {
if (!param) return EINVAL;
attr->__schedparam = param->sched_priority;
return 0;
}

View file

@ -0,0 +1,40 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/str/str.h"
#include "libc/thread/thread2.h"
/**
* Sets signal mask on thread attributes object.
*
* @param attr is the thread attributes object
* @param sigmask will be copied into attributes, or if it's null, then
* the existing signal mask presence on the object will be cleared
* @return 0 on success, or errno on error
*/
errno_t pthread_attr_setsigmask_np(pthread_attr_t *attr,
const sigset_t *sigmask) {
_Static_assert(sizeof(attr->__sigmask) == sizeof(*sigmask), "");
if (sigmask) {
attr->__havesigmask = true;
memcpy(attr->__sigmask, sigmask, sizeof(*sigmask));
} else {
attr->__havesigmask = false;
}
return 0;
}

View file

@ -32,9 +32,11 @@
#include "libc/runtime/clone.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/ss.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/spawn.h"
@ -84,7 +86,7 @@ static int PosixThread(void *arg, int tid) {
// set long jump handler so pthread_exit can bring control back here
if (!setjmp(pt->exiter)) {
__get_tls()->tib_pthread = (pthread_t)pt;
_sigsetmask(pt->sigmask);
_npassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0));
rc = pt->start(pt->arg);
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
_npassert(!pt->cleanup);
@ -224,6 +226,10 @@ static errno_t pthread_create_impl(pthread_t *thread,
}
// set initial status
if (!pt->attr.__havesigmask) {
pt->attr.__havesigmask = true;
memcpy(pt->attr.__sigmask, &oldsigs, sizeof(oldsigs));
}
switch (pt->attr.__detachstate) {
case PTHREAD_CREATE_JOINABLE:
atomic_store_explicit(&pt->status, kPosixThreadJoinable,
@ -246,7 +252,6 @@ static errno_t pthread_create_impl(pthread_t *thread,
pthread_spin_unlock(&_pthread_lock);
// launch PosixThread(pt) in new thread
pt->sigmask = oldsigs;
if ((rc = clone(PosixThread, pt->attr.__stackaddr,
pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0),
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES |
@ -260,9 +265,7 @@ static errno_t pthread_create_impl(pthread_t *thread,
return rc;
}
if (thread) {
*thread = (pthread_t)pt;
}
*thread = (pthread_t)pt;
return 0;
}

View file

@ -71,6 +71,6 @@ errno_t pthread_getaffinity_np(pthread_t thread, size_t size,
}
STRACE("pthread_getaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset,
DescribeErrnoResult(rc));
DescribeErrno(rc));
return rc;
}

View file

@ -16,13 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/thread/wait0.internal.h"
#include "libc/thread/thread2.h"
/**
* Waits for thread to terminate.
@ -44,24 +38,5 @@
* @threadsafe
*/
errno_t pthread_join(pthread_t thread, void **value_ptr) {
errno_t rc;
struct PosixThread *pt;
enum PosixThreadStatus status;
pt = (struct PosixThread *)thread;
status = atomic_load_explicit(&pt->status, memory_order_acquire);
// "The behavior is undefined if the value specified by the thread
// argument to pthread_join() does not refer to a joinable thread."
// ──Quoth POSIX.1-2017
_unassert(status == kPosixThreadJoinable || status == kPosixThreadTerminated);
if (!(rc = _wait0(&pt->tib->tib_tid))) {
pthread_spin_lock(&_pthread_lock);
_pthread_list = nsync_dll_remove_(_pthread_list, &pt->list);
pthread_spin_unlock(&_pthread_lock);
if (value_ptr) {
*value_ptr = pt->rc;
}
_pthread_free(pt);
pthread_decimate_np();
}
return 0;
return pthread_timedjoin_np(thread, value_ptr, 0);
}

View file

@ -22,7 +22,7 @@
/**
* Returns true if calling thread is the only thread.
*/
bool pthread_orphan_np(void) {
int pthread_orphan_np(void) {
bool res;
pthread_spin_lock(&_pthread_lock);
res = _pthread_list == _pthread_list->prev &&

View file

@ -74,6 +74,6 @@ errno_t pthread_setaffinity_np(pthread_t thread, size_t size,
}
}
STRACE("pthread_setaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset,
DescribeErrnoResult(rc));
DescribeErrno(rc));
return rc;
}

View file

@ -44,7 +44,6 @@
errno_t pthread_setschedparam(pthread_t thread, int policy,
const struct sched_param *param) {
struct PosixThread *pt = (struct PosixThread *)thread;
if (!param) return EINVAL;
pt->attr.__schedpolicy = policy;
pt->attr.__schedparam = param->sched_priority;
return _pthread_reschedule(pt);

View file

@ -0,0 +1,72 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/struct/timespec.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread2.h"
#include "libc/thread/tls.h"
#include "libc/thread/wait0.internal.h"
/**
* Waits for thread to terminate.
*
* Multiple threads joining the same thread is undefined behavior. If a
* deferred or masked cancellation happens to the calling thread either
* before or during the waiting process then the target thread will not
* be joined. Calling pthread_join() on a non-joinable thread, e.g. one
* that's been detached, is undefined behavior. If a thread attempts to
* join itself, then the behavior is undefined.
*
* @param value_ptr if non-null will receive pthread_exit() argument
* if the thread called pthread_exit(), or `PTHREAD_CANCELED` if
* pthread_cancel() destroyed the thread instead
* @param abstime specifies an absolute deadline or the timestamp of
* when we'll stop waiting; if this is null we will wait forever
* @return 0 on success, or errno on error
* @raise ECANCELED if calling thread was cancelled in masked mode
* @raise EBUSY if `abstime` deadline elapsed
* @cancellationpoint
* @returnserrno
* @threadsafe
*/
errno_t pthread_timedjoin_np(pthread_t thread, void **value_ptr,
struct timespec *abstime) {
errno_t rc;
struct PosixThread *pt;
enum PosixThreadStatus status;
pt = (struct PosixThread *)thread;
status = atomic_load_explicit(&pt->status, memory_order_acquire);
// "The behavior is undefined if the value specified by the thread
// argument to pthread_join() does not refer to a joinable thread."
// ──Quoth POSIX.1-2017
_unassert(status == kPosixThreadJoinable || status == kPosixThreadTerminated);
if (!(rc = _wait0(&pt->tib->tib_tid, abstime))) {
pthread_spin_lock(&_pthread_lock);
_pthread_list = nsync_dll_remove_(_pthread_list, &pt->list);
pthread_spin_unlock(&_pthread_lock);
if (value_ptr) {
*value_ptr = pt->rc;
}
_pthread_free(pt);
pthread_decimate_np();
}
return 0;
}

View file

@ -0,0 +1,42 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/thread2.h"
/**
* Joins thread if it's already terminated.
*
* Multiple threads joining the same thread is undefined behavior. If a
* deferred or masked cancellation happens to the calling thread either
* before or during the waiting process then the target thread will not
* be joined. Calling pthread_join() on a non-joinable thread, e.g. one
* that's been detached, is undefined behavior. If a thread attempts to
* join itself, then the behavior is undefined.
*
* @param value_ptr if non-null will receive pthread_exit() argument
* if the thread called pthread_exit(), or `PTHREAD_CANCELED` if
* pthread_cancel() destroyed the thread instead
* @return 0 on success, or errno on error
* @raise ECANCELED if calling thread was cancelled in masked mode
* @cancellationpoint
* @returnserrno
* @threadsafe
*/
errno_t pthread_tryjoin_np(pthread_t thread, void **value_ptr) {
return pthread_timedjoin_np(thread, value_ptr, &timespec_zero);
}

View file

@ -34,7 +34,7 @@
int sem_post(sem_t *sem) {
int rc, old, wakeups;
_unassert(sem->sem_pshared || sem->sem_pid == getpid());
old = atomic_fetch_add_explicit(&sem->sem_value, 1, memory_order_relaxed);
old = atomic_fetch_add_explicit(&sem->sem_value, 1, memory_order_acq_rel);
_unassert(old > INT_MIN);
if (old >= 0) {
wakeups = nsync_futex_wake_(&sem->sem_value, 1, sem->sem_pshared);

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
@ -148,7 +149,7 @@ int _join(struct spawn *th) {
int rc;
if (th->tib) {
// wait for ctid to become zero
_wait0(&th->tib->tib_tid);
_npassert(!_wait0(&th->tib->tib_tid, 0));
// free thread memory
free(th->tls);
rc = munmap(th->stk, GetStackSize());

View file

@ -35,6 +35,8 @@
#define PTHREAD_SCOPE_SYSTEM 0
#define PTHREAD_SCOPE_PROCESS 1
#define PTHREAD_ATTR_NO_SIGMASK_NP -1
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -58,7 +60,7 @@ typedef struct pthread_once_s {
} pthread_once_t;
typedef struct pthread_spinlock_s {
_Atomic(char) _lock;
_Atomic(int) _lock;
} pthread_spinlock_t;
typedef struct pthread_mutex_s {
@ -91,11 +93,13 @@ typedef struct pthread_barrier_s {
typedef struct pthread_attr_s {
char __detachstate;
char __inheritsched;
char __havesigmask;
int __schedparam;
int __schedpolicy;
int __contentionscope;
unsigned __guardsize;
unsigned __stacksize;
uint32_t __sigmask[4];
char *__stackaddr;
} pthread_attr_t;
@ -106,108 +110,109 @@ struct _pthread_cleanup_buffer {
struct _pthread_cleanup_buffer *__prev;
};
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *),
void *);
/* clang-format off */
int pthread_yield(void);
bool pthread_orphan_np(void);
void pthread_testcancel(void);
void pthread_decimate_np(void);
int pthread_testcancel_np(void);
void pthread_exit(void *) wontreturn;
pthread_t pthread_self(void) pureconst;
int pthread_print_np(int, const char *, ...);
pthread_id_np_t pthread_getthreadid_np(void);
int pthread_getunique_np(pthread_t, pthread_id_np_t *);
int pthread_setname_np(pthread_t, const char *);
int pthread_getname_np(pthread_t, char *, size_t);
int pthread_getattr_np(pthread_t, pthread_attr_t *);
int pthread_attr_init(pthread_attr_t *);
int pthread_attr_destroy(pthread_attr_t *);
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
int pthread_attr_setdetachstate(pthread_attr_t *, int);
int pthread_attr_getguardsize(const pthread_attr_t *, size_t *);
int pthread_attr_setguardsize(pthread_attr_t *, size_t);
int pthread_attr_getinheritsched(const pthread_attr_t *, int *);
int pthread_attr_setinheritsched(pthread_attr_t *, int);
int pthread_attr_getschedpolicy(const pthread_attr_t *, int *);
int pthread_attr_setschedpolicy(pthread_attr_t *, int);
int pthread_attr_getscope(const pthread_attr_t *, int *);
int pthread_attr_setscope(pthread_attr_t *, int);
int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *);
int pthread_attr_setstack(pthread_attr_t *, void *, size_t);
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *);
int pthread_attr_setstacksize(pthread_attr_t *, size_t);
int pthread_detach(pthread_t);
int pthread_kill(pthread_t, int);
int pthread_cancel(pthread_t);
int pthread_setcanceltype(int, int *);
int pthread_setcancelstate(int, int *);
int pthread_setschedprio(pthread_t, int);
int pthread_join(pthread_t, void **);
int pthread_equal(pthread_t, pthread_t);
int pthread_once(pthread_once_t *, void (*)(void));
int pthread_spin_init(pthread_spinlock_t *, int);
int pthread_spin_destroy(pthread_spinlock_t *);
int pthread_spin_lock(pthread_spinlock_t *);
int pthread_spin_unlock(pthread_spinlock_t *);
int pthread_spin_trylock(pthread_spinlock_t *);
int pthread_mutexattr_init(pthread_mutexattr_t *);
int pthread_mutexattr_destroy(pthread_mutexattr_t *);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *);
int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *);
int pthread_atfork(void (*)(void), void (*)(void), void (*)(void));
int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_destroy(pthread_mutex_t *);
int pthread_mutex_consistent(pthread_mutex_t *);
int pthread_condattr_init(pthread_condattr_t *);
int pthread_condattr_destroy(pthread_condattr_t *);
int pthread_condattr_setpshared(pthread_condattr_t *, int);
int pthread_condattr_getpshared(const pthread_condattr_t *, int *);
int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *);
int pthread_cond_destroy(pthread_cond_t *);
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *);
int pthread_cond_broadcast(pthread_cond_t *);
int pthread_cond_signal(pthread_cond_t *);
int pthread_rwlockattr_init(pthread_rwlockattr_t *);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int);
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *, int *);
int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *);
int pthread_rwlock_destroy(pthread_rwlock_t *);
int pthread_rwlock_rdlock(pthread_rwlock_t *);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
int pthread_rwlock_wrlock(pthread_rwlock_t *);
int pthread_rwlock_trywrlock(pthread_rwlock_t *);
int pthread_rwlock_unlock(pthread_rwlock_t *);
int pthread_key_create(pthread_key_t *, pthread_key_dtor);
int pthread_attr_destroy(pthread_attr_t *) paramsnonnull();
int pthread_attr_getdetachstate(const pthread_attr_t *, int *) paramsnonnull();
int pthread_attr_getguardsize(const pthread_attr_t *, size_t *) paramsnonnull();
int pthread_attr_getinheritsched(const pthread_attr_t *, int *) paramsnonnull();
int pthread_attr_getschedpolicy(const pthread_attr_t *, int *) paramsnonnull();
int pthread_attr_getscope(const pthread_attr_t *, int *) paramsnonnull();
int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *) paramsnonnull();
int pthread_attr_getstacksize(const pthread_attr_t *, size_t *) paramsnonnull();
int pthread_attr_init(pthread_attr_t *) paramsnonnull();
int pthread_attr_setdetachstate(pthread_attr_t *, int) paramsnonnull();
int pthread_attr_setguardsize(pthread_attr_t *, size_t) paramsnonnull();
int pthread_attr_setinheritsched(pthread_attr_t *, int) paramsnonnull();
int pthread_attr_setschedpolicy(pthread_attr_t *, int) paramsnonnull();
int pthread_attr_setscope(pthread_attr_t *, int) paramsnonnull();
int pthread_attr_setstack(pthread_attr_t *, void *, size_t) paramsnonnull((1));
int pthread_attr_setstacksize(pthread_attr_t *, size_t) paramsnonnull();
int pthread_barrier_destroy(pthread_barrier_t *) paramsnonnull();
int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, unsigned) paramsnonnull((1));
int pthread_barrier_wait(pthread_barrier_t *) paramsnonnull();
int pthread_barrierattr_destroy(pthread_barrierattr_t *) paramsnonnull();
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *, int *) paramsnonnull();
int pthread_barrierattr_init(pthread_barrierattr_t *) paramsnonnull();
int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int) paramsnonnull();
int pthread_cancel(pthread_t);
int pthread_cond_broadcast(pthread_cond_t *) paramsnonnull();
int pthread_cond_destroy(pthread_cond_t *) paramsnonnull();
int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *) paramsnonnull((1));
int pthread_cond_signal(pthread_cond_t *) paramsnonnull();
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *) paramsnonnull();
int pthread_condattr_destroy(pthread_condattr_t *) paramsnonnull();
int pthread_condattr_getpshared(const pthread_condattr_t *, int *) paramsnonnull();
int pthread_condattr_init(pthread_condattr_t *) paramsnonnull();
int pthread_condattr_setpshared(pthread_condattr_t *, int) paramsnonnull();
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *) paramsnonnull((1, 3));
int pthread_detach(pthread_t);
int pthread_equal(pthread_t, pthread_t);
int pthread_getattr_np(pthread_t, pthread_attr_t *) paramsnonnull();
int pthread_getname_np(pthread_t, char *, size_t) paramsnonnull();
int pthread_getunique_np(pthread_t, pthread_id_np_t *) paramsnonnull();
int pthread_join(pthread_t, void **);
int pthread_key_create(pthread_key_t *, pthread_key_dtor) paramsnonnull((1));
int pthread_key_delete(pthread_key_t);
int pthread_kill(pthread_t, int);
int pthread_mutex_consistent(pthread_mutex_t *) paramsnonnull();
int pthread_mutex_destroy(pthread_mutex_t *) paramsnonnull();
int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *) paramsnonnull((1));
int pthread_mutex_lock(pthread_mutex_t *) paramsnonnull();
int pthread_mutex_trylock(pthread_mutex_t *) paramsnonnull();
int pthread_mutex_unlock(pthread_mutex_t *) paramsnonnull();
int pthread_mutexattr_destroy(pthread_mutexattr_t *) paramsnonnull();
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *) paramsnonnull();
int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *) paramsnonnull();
int pthread_mutexattr_init(pthread_mutexattr_t *) paramsnonnull();
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int) paramsnonnull();
int pthread_mutexattr_settype(pthread_mutexattr_t *, int) paramsnonnull();
int pthread_once(pthread_once_t *, void (*)(void)) paramsnonnull();
int pthread_orphan_np(void);
int pthread_print_np(int, const char *, ...);
int pthread_rwlock_destroy(pthread_rwlock_t *) paramsnonnull();
int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *) paramsnonnull((1));
int pthread_rwlock_rdlock(pthread_rwlock_t *) paramsnonnull();
int pthread_rwlock_tryrdlock(pthread_rwlock_t *) paramsnonnull();
int pthread_rwlock_trywrlock(pthread_rwlock_t *) paramsnonnull();
int pthread_rwlock_unlock(pthread_rwlock_t *) paramsnonnull();
int pthread_rwlock_wrlock(pthread_rwlock_t *) paramsnonnull();
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *) paramsnonnull();
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *, int *) paramsnonnull();
int pthread_rwlockattr_init(pthread_rwlockattr_t *) paramsnonnull();
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int) paramsnonnull();
int pthread_setcancelstate(int, int *);
int pthread_setcanceltype(int, int *);
int pthread_setname_np(pthread_t, const char *) paramsnonnull();
int pthread_setschedprio(pthread_t, int);
int pthread_setspecific(pthread_key_t, const void *);
int pthread_spin_destroy(pthread_spinlock_t *) paramsnonnull();
int pthread_spin_init(pthread_spinlock_t *, int) paramsnonnull();
int pthread_spin_lock(pthread_spinlock_t *) paramsnonnull();
int pthread_spin_trylock(pthread_spinlock_t *) paramsnonnull();
int pthread_spin_unlock(pthread_spinlock_t *) paramsnonnull();
int pthread_testcancel_np(void);
int pthread_tryjoin_np(pthread_t, void **);
int pthread_yield(void);
pthread_id_np_t pthread_getthreadid_np(void);
pthread_t pthread_self(void) pureconst;
void *pthread_getspecific(pthread_key_t);
int pthread_barrierattr_init(pthread_barrierattr_t *);
int pthread_barrierattr_destroy(pthread_barrierattr_t *);
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *, int *);
int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int);
int pthread_barrier_wait(pthread_barrier_t *);
int pthread_barrier_destroy(pthread_barrier_t *);
int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *,
unsigned);
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *, int);
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *, void (*)(void *),
void *);
void pthread_cleanup_pop(struct _pthread_cleanup_buffer *, int) paramsnonnull();
void pthread_cleanup_push(struct _pthread_cleanup_buffer *, void (*)(void *), void *) paramsnonnull((1));
void pthread_decimate_np(void);
void pthread_exit(void *) wontreturn;
void pthread_testcancel(void);
/* clang-format on */
#define pthread_cleanup_push(routine, arg) \
{ \
struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push(&_buffer, (routine), (arg));
pthread_cleanup_push(&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop(&_buffer, (execute)); \
#define pthread_cleanup_pop(execute) \
pthread_cleanup_pop(&_buffer, (execute)); \
}
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 && \

View file

@ -2,21 +2,26 @@
#define COSMOPOLITAN_LIBC_INTRIN_PTHREAD2_H_
#include "libc/calls/struct/cpuset.h"
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/timespec.h"
#include "libc/runtime/stack.h"
#include "libc/thread/thread.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/* clang-format off */
int pthread_attr_getschedparam(const pthread_attr_t *, struct sched_param *);
int pthread_attr_setschedparam(pthread_attr_t *, const struct sched_param *);
int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *,
const struct timespec *);
int pthread_setaffinity_np(pthread_t, size_t, const cpu_set_t *);
int pthread_getaffinity_np(pthread_t, size_t, cpu_set_t *);
int pthread_setschedparam(pthread_t, int, const struct sched_param *);
int pthread_getschedparam(pthread_t, int *, struct sched_param *);
int pthread_attr_getschedparam(const pthread_attr_t *, struct sched_param *) paramsnonnull();
int pthread_attr_getsigmask_np(const pthread_attr_t *, sigset_t *) paramsnonnull((1));
int pthread_attr_setschedparam(pthread_attr_t *, const struct sched_param *) paramsnonnull();
int pthread_attr_setsigmask_np(pthread_attr_t *, const sigset_t *) paramsnonnull((1));
int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, const struct timespec *) paramsnonnull((1, 2));
int pthread_getaffinity_np(pthread_t, size_t, cpu_set_t *) paramsnonnull();
int pthread_getschedparam(pthread_t, int *, struct sched_param *) paramsnonnull();
int pthread_setaffinity_np(pthread_t, size_t, const cpu_set_t *) paramsnonnull();
int pthread_setschedparam(pthread_t, int, const struct sched_param *) paramsnonnull();
int pthread_timedjoin_np(pthread_t, void **, struct timespec *);
/* clang-format off */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_PTHREAD2_H_ */

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
@ -37,9 +38,10 @@
*
* @return 0 on success, or errno on error
* @raise ECANCELED if calling thread was cancelled in masked mode
* @raise EBUSY if `abstime` was specified and deadline expired
* @cancellationpoint
*/
errno_t _wait0(const atomic_int *ctid) {
errno_t _wait0(const atomic_int *ctid, struct timespec *abstime) {
int x, rc = 0;
// "The behavior is undefined if the value specified by the thread
// argument to pthread_join() refers to the calling thread."
@ -50,9 +52,13 @@ errno_t _wait0(const atomic_int *ctid) {
if (!(rc = pthread_testcancel_np())) {
BEGIN_CANCELLATION_POINT;
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
if (nsync_futex_wait_(ctid, x, !IsWindows(), 0) == -ECANCELED) {
rc = nsync_futex_wait_(ctid, x, !IsWindows(), abstime);
if (rc == -ECANCELED) {
rc = ECANCELED;
break;
} else if (rc == -ETIMEDOUT) {
rc = EBUSY;
break;
}
}
END_CANCELLATION_POINT;

View file

@ -1,10 +1,11 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_WAIT0_H_
#define COSMOPOLITAN_LIBC_INTRIN_WAIT0_H_
#include "libc/atomic.h"
#include "libc/calls/struct/timespec.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
errno_t _wait0(const atomic_int *) _Hide;
errno_t _wait0(const atomic_int *, struct timespec *) _Hide;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */