mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-08 12:18:31 +00:00
Make futexes cancellable by pthreads
This commit is contained in:
parent
2278327eba
commit
022536cab6
101 changed files with 627 additions and 391 deletions
|
@ -48,7 +48,6 @@ struct _umtx_time {
|
|||
uint32_t _clockid;
|
||||
};
|
||||
|
||||
int sys_umtx_op(void *, int, unsigned long, void *, void *);
|
||||
int sys_umtx_timedwait_uint(_Atomic(int) *, int, bool, const struct timespec *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
// LEGAL TRANSITIONS ┌──> TERMINATED
|
||||
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE
|
||||
// LEGAL TRANSITIONS ┌──> TERMINATED ─┐
|
||||
// pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ───┴─> ZOMBIE
|
||||
// └──────────────┘
|
||||
enum PosixThreadStatus {
|
||||
|
||||
|
@ -47,6 +47,8 @@ enum PosixThreadStatus {
|
|||
//
|
||||
// - kPosixThreadTerminated -> _pthread_free() will happen when
|
||||
// pthread_join() is called by the user.
|
||||
// - kPosixThreadTerminated -> kPosixThreadZombie will happen when
|
||||
// pthread_detach() is called by the user.
|
||||
kPosixThreadTerminated,
|
||||
|
||||
// this is a detached thread that terminated.
|
||||
|
@ -83,10 +85,10 @@ extern _Atomic(pthread_key_dtor) _pthread_key_dtor[PTHREAD_KEYS_MAX] hidden;
|
|||
int _pthread_atfork(atfork_f, atfork_f, atfork_f) hidden;
|
||||
int _pthread_reschedule(struct PosixThread *) hidden;
|
||||
int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) hidden;
|
||||
int _pthread_wait(struct PosixThread *) hidden;
|
||||
void _pthread_free(struct PosixThread *) hidden;
|
||||
void _pthread_cleanup(struct PosixThread *) hidden;
|
||||
void _pthread_ungarbage(void) hidden;
|
||||
void _pthread_wait(struct PosixThread *) hidden;
|
||||
void _pthread_zombies_add(struct PosixThread *) hidden;
|
||||
void _pthread_zombies_purge(void) hidden;
|
||||
void _pthread_zombies_decimate(void) hidden;
|
||||
|
|
|
@ -65,8 +65,11 @@ void _pthread_onfork_parent(void) {
|
|||
}
|
||||
|
||||
void _pthread_onfork_child(void) {
|
||||
pthread_mutexattr_t attr;
|
||||
extern pthread_mutex_t __mmi_lock_obj;
|
||||
bzero(&__mmi_lock_obj, sizeof(__mmi_lock_obj));
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&__mmi_lock_obj, &attr);
|
||||
__kmalloc_unlock();
|
||||
_pthread_onfork(2);
|
||||
}
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
|
||||
/**
|
||||
* Destroys pthread attributes.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int pthread_attr_destroy(pthread_attr_t *attr) {
|
||||
errno_t pthread_attr_destroy(pthread_attr_t *attr) {
|
||||
memset(attr, -1, sizeof(*attr));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
* - `PTHREAD_CREATE_DETACHED`
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
|
||||
errno_t pthread_attr_getdetachstate(const pthread_attr_t *attr,
|
||||
int *detachstate) {
|
||||
*detachstate = attr->__detachstate;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
/**
|
||||
* Returns thread inherit schedule attribute.
|
||||
*/
|
||||
int pthread_attr_getinheritsched(const pthread_attr_t *attr,
|
||||
int *inheritsched) {
|
||||
errno_t pthread_attr_getinheritsched(const pthread_attr_t *attr,
|
||||
int *inheritsched) {
|
||||
*inheritsched = attr->__inheritsched;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
/**
|
||||
* Gets thread scheduler parameter attribute.
|
||||
*/
|
||||
int pthread_attr_getschedparam(const pthread_attr_t *attr,
|
||||
struct sched_param *param) {
|
||||
errno_t pthread_attr_getschedparam(const pthread_attr_t *attr,
|
||||
struct sched_param *param) {
|
||||
*param = (struct sched_param){attr->__schedparam};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/**
|
||||
* Gets thread scheduler policy attribute
|
||||
*/
|
||||
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) {
|
||||
errno_t pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) {
|
||||
*policy = attr->__schedpolicy;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope) {
|
||||
errno_t pthread_attr_getscope(const pthread_attr_t *attr,
|
||||
int *contentionscope) {
|
||||
*contentionscope = attr->__contentionscope;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raises EINVAL if `detachstate` is invalid
|
||||
*/
|
||||
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
|
||||
errno_t pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
|
||||
switch (detachstate) {
|
||||
case PTHREAD_CREATE_JOINABLE:
|
||||
case PTHREAD_CREATE_DETACHED:
|
||||
|
|
|
@ -40,8 +40,8 @@
|
|||
* @see sched_get_priority_max()
|
||||
* @see sched_setparam()
|
||||
*/
|
||||
int pthread_attr_setschedparam(pthread_attr_t *attr,
|
||||
const struct sched_param *param) {
|
||||
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;
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
* supported (Linux), otherwise it's treated as `SCHED_OTHER`
|
||||
* @see sched_setscheduler()
|
||||
*/
|
||||
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) {
|
||||
errno_t pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) {
|
||||
attr->__schedpolicy = policy;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* @raise ENOTSUP if `contentionscope` isn't supported on host OS
|
||||
* @raise EINVAL if `contentionscope` was invalid
|
||||
*/
|
||||
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) {
|
||||
errno_t pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) {
|
||||
switch (contentionscope) {
|
||||
case PTHREAD_SCOPE_SYSTEM:
|
||||
attr->__contentionscope = contentionscope;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raise EINVAL if threads are still inside the barrier
|
||||
*/
|
||||
int pthread_barrier_destroy(pthread_barrier_t *barrier) {
|
||||
errno_t pthread_barrier_destroy(pthread_barrier_t *barrier) {
|
||||
if (barrier->_nsync) {
|
||||
nsync_counter_free(barrier->_nsync);
|
||||
barrier->_nsync = 0;
|
||||
|
|
|
@ -30,8 +30,9 @@
|
|||
* @raise EINVAL if `count` isn't greater than zero
|
||||
* @raise ENOMEM if insufficient memory exists
|
||||
*/
|
||||
int pthread_barrier_init(pthread_barrier_t *barrier,
|
||||
const pthread_barrierattr_t *attr, unsigned count) {
|
||||
errno_t pthread_barrier_init(pthread_barrier_t *barrier,
|
||||
const pthread_barrierattr_t *attr,
|
||||
unsigned count) {
|
||||
nsync_counter c;
|
||||
if (!count) return EINVAL;
|
||||
if (!(c = nsync_counter_new(count))) return ENOMEM;
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Destroys barrier attributes.
|
||||
*
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
|
||||
errno_t pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
|
||||
memset(attr, -1, sizeof(*attr));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
|
||||
int *pshared) {
|
||||
errno_t pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = *attr;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
*
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
|
||||
errno_t pthread_barrierattr_init(pthread_barrierattr_t *attr) {
|
||||
*attr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raises EINVAL if `pshared` is invalid
|
||||
*/
|
||||
int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) {
|
||||
errno_t pthread_barrierattr_setpshared(pthread_barrierattr_t *attr,
|
||||
int pshared) {
|
||||
switch (pshared) {
|
||||
case PTHREAD_PROCESS_PRIVATE:
|
||||
*attr = pshared;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
|
@ -85,12 +86,12 @@ static void ListenForSigCancel(void) {
|
|||
* `writev`, `pwrite`, `pwritev`, `accept`, `connect`, `recvmsg`,
|
||||
* `sendmsg`, `recv`, `send`, `tcdrain`, `clock_nanosleep`, `fsync`,
|
||||
* `fdatasync`, `fcntl(F_SETLKW)`, `epoll`, `sigsuspend`, `msync`,
|
||||
* `wait4`, `getrandom`, `pthread_cond_timedwait` are most cancellation
|
||||
* points, plus many userspace libraries that call the above functions,
|
||||
* unless they're using pthread_setcancelstate() to temporarily disable
|
||||
* the cancellation mechanism. Some userspace functions, e.g. system()
|
||||
* and popen() will eagerly call pthread_testcancel_np() to help avoid
|
||||
* the potential for resource leaks later on.
|
||||
* `wait4`, `getrandom`, `pthread_cond_timedwait`, and `sem_timedwait`
|
||||
* are most cancellation points, plus many userspace libraries that call
|
||||
* the above functions, unless they're using pthread_setcancelstate() to
|
||||
* temporarily disable the cancellation mechanism. Some userspace
|
||||
* functions, e.g. system() will eagerly call pthread_testcancel_np() to
|
||||
* help avoid the potential for resource leaks later on.
|
||||
*
|
||||
* It's possible to put a thread in asynchronous cancellation mode using
|
||||
* pthread_setcanceltype(), thus allowing a cancellation to occur at any
|
||||
|
@ -110,7 +111,7 @@ static void ListenForSigCancel(void) {
|
|||
* @return 0 on success, or errno on error
|
||||
* @raise ESRCH if thread isn't alive
|
||||
*/
|
||||
int pthread_cancel(pthread_t thread) {
|
||||
errno_t pthread_cancel(pthread_t thread) {
|
||||
int e, rc, tid;
|
||||
static bool once;
|
||||
struct PosixThread *pt;
|
||||
|
@ -124,9 +125,9 @@ int pthread_cancel(pthread_t thread) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
atomic_store_explicit(&pt->cancelled, 1, memory_order_release);
|
||||
atomic_exchange_explicit(&pt->cancelled, 1, memory_order_release);
|
||||
if (thread == __get_tls()->tib_pthread) {
|
||||
if (!(pt->flags & PT_NOCANCEL) && (pt->flags & PT_ASYNC)) {
|
||||
if (!(pt->flags & (PT_NOCANCEL | PT_MASKED)) && (pt->flags & PT_ASYNC)) {
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
return 0;
|
||||
|
@ -183,7 +184,7 @@ void pthread_testcancel(void) {
|
|||
* @return 0 if not cancelled or cancellation is blocked or `ECANCELED`
|
||||
* in masked mode when the calling thread has been cancelled
|
||||
*/
|
||||
int pthread_testcancel_np(void) {
|
||||
errno_t pthread_testcancel_np(void) {
|
||||
int rc;
|
||||
struct PosixThread *pt;
|
||||
if (!__tls_enabled) return 0;
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*-*- 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/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *cb, int execute) {
|
||||
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
_unassert(cb == pt->cleanup);
|
||||
pt->cleanup = cb->__prev;
|
||||
if (execute) {
|
||||
cb->__routine(cb->__arg);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*-*- 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/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
void _pthread_cleanup_push(struct _pthread_cleanup_buffer *cb,
|
||||
void (*routine)(void *), void *arg) {
|
||||
struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
cb->__routine = routine;
|
||||
cb->__arg = arg;
|
||||
cb->__prev = pt->cleanup;
|
||||
pt->cleanup = cb;
|
||||
}
|
|
@ -25,7 +25,7 @@
|
|||
* @return 0 on success, or error number on failure
|
||||
* @raise EINVAL if threads are still waiting on condition
|
||||
*/
|
||||
int pthread_cond_destroy(pthread_cond_t *cond) {
|
||||
errno_t pthread_cond_destroy(pthread_cond_t *cond) {
|
||||
memset(cond, -1, sizeof(*cond));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
* @param attr may be null
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) {
|
||||
errno_t pthread_cond_init(pthread_cond_t *cond,
|
||||
const pthread_condattr_t *attr) {
|
||||
*cond = (pthread_cond_t){0};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -41,8 +41,9 @@
|
|||
* @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock
|
||||
* isn't owned by the current thread
|
||||
* @raise EINVAL if `0 ≤ abstime->tv_nsec < 1000000000` wasn't the case
|
||||
* @see pthread_cond_broadcast
|
||||
* @see pthread_cond_signal
|
||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||
* @see pthread_cond_broadcast()
|
||||
* @see pthread_cond_signal()
|
||||
* @cancellationpoint
|
||||
*/
|
||||
errno_t pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
*
|
||||
* @param mutex needs to be held by thread when calling this function
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||
* @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock
|
||||
* isn't owned by the current thread
|
||||
* @see pthread_cond_timedwait
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Destroys condition attributes.
|
||||
*
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_condattr_destroy(pthread_condattr_t *attr) {
|
||||
errno_t pthread_condattr_destroy(pthread_condattr_t *attr) {
|
||||
memset(attr, -1, sizeof(*attr));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
* - `PTHREAD_PROCESS_SHARED`
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) {
|
||||
errno_t pthread_condattr_getpshared(const pthread_condattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = *attr;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
*
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_condattr_init(pthread_condattr_t *attr) {
|
||||
errno_t pthread_condattr_init(pthread_condattr_t *attr) {
|
||||
*attr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raises EINVAL if `pshared` is invalid
|
||||
*/
|
||||
int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
|
||||
errno_t pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
|
||||
switch (pshared) {
|
||||
case PTHREAD_PROCESS_PRIVATE:
|
||||
*attr = pshared;
|
||||
|
|
|
@ -59,8 +59,8 @@ STATIC_YOINK("_pthread_atfork");
|
|||
#define MAP_ANON_OPENBSD 0x1000
|
||||
#define MAP_STACK_OPENBSD 0x4000
|
||||
|
||||
void _pthread_wait(struct PosixThread *pt) {
|
||||
_wait0(&pt->tib->tib_tid);
|
||||
errno_t _pthread_wait(struct PosixThread *pt) {
|
||||
return _wait0(&pt->tib->tib_tid);
|
||||
}
|
||||
|
||||
void _pthread_free(struct PosixThread *pt) {
|
||||
|
|
|
@ -32,36 +32,29 @@
|
|||
* @returnserrno
|
||||
* @threadsafe
|
||||
*/
|
||||
int pthread_detach(pthread_t thread) {
|
||||
errno_t pthread_detach(pthread_t thread) {
|
||||
struct PosixThread *pt;
|
||||
enum PosixThreadStatus status;
|
||||
if (!(pt = (struct PosixThread *)thread)) {
|
||||
return EINVAL;
|
||||
}
|
||||
enum PosixThreadStatus status, transition;
|
||||
if (!(pt = (struct PosixThread *)thread)) return EINVAL;
|
||||
for (;;) {
|
||||
status = atomic_load_explicit(&pt->status, memory_order_acquire);
|
||||
if (status == kPosixThreadDetached || status == kPosixThreadZombie) {
|
||||
// these two states indicate the thread was already detached, in
|
||||
// which case it's already listed under _pthread_zombies.
|
||||
return EINVAL;
|
||||
} else if (status == kPosixThreadTerminated) {
|
||||
// thread was joinable and finished running. since pthread_join
|
||||
// won't be called, it's safe to free the thread resources now.
|
||||
// POSIX says this could be reported as ESRCH but then our test
|
||||
// code would be less elegant in order for it to avoid flaking.
|
||||
_pthread_wait(pt);
|
||||
_pthread_free(pt);
|
||||
break;
|
||||
} else if (status == kPosixThreadJoinable) {
|
||||
if (atomic_compare_exchange_weak_explicit(
|
||||
&pt->status, &status, kPosixThreadDetached, memory_order_release,
|
||||
memory_order_relaxed)) {
|
||||
_pthread_zombies_add(pt);
|
||||
break;
|
||||
}
|
||||
transition = kPosixThreadDetached;
|
||||
} else if (status == kPosixThreadTerminated) {
|
||||
transition = kPosixThreadZombie;
|
||||
} else {
|
||||
notpossible;
|
||||
}
|
||||
if (atomic_compare_exchange_weak_explicit(&pt->status, &status, transition,
|
||||
memory_order_release,
|
||||
memory_order_relaxed)) {
|
||||
_pthread_zombies_add(pt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
* @return 0 on success, or errno on error
|
||||
* @raise ENOMEM is listed as a possible result by LSB 5.0
|
||||
*/
|
||||
int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
|
||||
errno_t pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
memcpy(attr, &pt->attr, sizeof(pt->attr));
|
||||
switch (atomic_load_explicit(&pt->status, memory_order_relaxed)) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -28,23 +29,7 @@
|
|||
#include "libc/sysv/consts/pr.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
/**
|
||||
* Gets name of thread registered with system, e.g.
|
||||
*
|
||||
* char name[64];
|
||||
* pthread_getname_np(thread, name, sizeof(name));
|
||||
*
|
||||
* If the thread doesn't have a name, then empty string is returned.
|
||||
* This implementation guarantees `buf` is always modified, even on
|
||||
* error, and will always be nul-terminated. If `size` is 0 then this
|
||||
* function returns 0. Your `buf` is also chomped to remove newlines.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ERANGE if `size` wasn't large enough, in which case your
|
||||
* result will still be returned truncated if possible
|
||||
* @raise ENOSYS on MacOS, Windows, FreeBSD, and OpenBSD
|
||||
*/
|
||||
errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
|
||||
static errno_t pthread_getname_impl(pthread_t thread, char *name, size_t size) {
|
||||
int fd, rc, tid, len, e = errno;
|
||||
|
||||
if (!size) return 0;
|
||||
|
@ -113,3 +98,27 @@ errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
|
|||
return ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets name of thread registered with system, e.g.
|
||||
*
|
||||
* char name[64];
|
||||
* pthread_getname_np(thread, name, sizeof(name));
|
||||
*
|
||||
* If the thread doesn't have a name, then empty string is returned.
|
||||
* This implementation guarantees `buf` is always modified, even on
|
||||
* error, and will always be nul-terminated. If `size` is 0 then this
|
||||
* function returns 0. Your `buf` is also chomped to remove newlines.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ERANGE if `size` wasn't large enough, in which case your
|
||||
* result will still be returned truncated if possible
|
||||
* @raise ENOSYS on MacOS, Windows, FreeBSD, and OpenBSD
|
||||
*/
|
||||
errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
|
||||
errno_t rc;
|
||||
BLOCK_CANCELLATIONS;
|
||||
rc = pthread_getname_impl(thread, name, size);
|
||||
ALLOW_CANCELLATIONS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
/**
|
||||
* Gets most recently set scheduling of thread.
|
||||
*/
|
||||
int pthread_getschedparam(pthread_t thread, int *policy,
|
||||
struct sched_param *param) {
|
||||
errno_t pthread_getschedparam(pthread_t thread, int *policy,
|
||||
struct sched_param *param) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
*policy = pt->attr.__schedpolicy;
|
||||
*param = (struct sched_param){pt->attr.__schedparam};
|
||||
|
|
|
@ -27,14 +27,16 @@
|
|||
* @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 with error
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||
* @raise EDEADLK if `thread` is the current thread
|
||||
* @raise EINVAL if `thread` is detached
|
||||
* @cancellationpoint
|
||||
* @returnserrno
|
||||
* @threadsafe
|
||||
*/
|
||||
int pthread_join(pthread_t thread, void **value_ptr) {
|
||||
errno_t pthread_join(pthread_t thread, void **value_ptr) {
|
||||
errno_t rc;
|
||||
struct PosixThread *pt;
|
||||
if (thread == __get_tls()->tib_pthread) {
|
||||
return EDEADLK;
|
||||
|
@ -44,7 +46,9 @@ int pthread_join(pthread_t thread, void **value_ptr) {
|
|||
pt->status == kPosixThreadDetached) {
|
||||
return EINVAL;
|
||||
}
|
||||
_pthread_wait(pt);
|
||||
if ((rc = _pthread_wait(pt))) {
|
||||
return rc;
|
||||
}
|
||||
if (value_ptr) {
|
||||
*value_ptr = pt->rc;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
* @raise EPERM if permission was denied
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int pthread_kill(pthread_t thread, int sig) {
|
||||
errno_t pthread_kill(pthread_t thread, int sig) {
|
||||
int rc, e = errno;
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
if (!tkill(pt->tid, sig)) {
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
* @return 0 on success, or error number on failure
|
||||
* @raise EINVAL if mutex is locked in our implementation
|
||||
*/
|
||||
int pthread_mutex_destroy(pthread_mutex_t *mutex) {
|
||||
errno_t pthread_mutex_destroy(pthread_mutex_t *mutex) {
|
||||
memset(mutex, -1, sizeof(*mutex));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Destroys mutex attr.
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
|
||||
errno_t pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
|
||||
memset(attr, -1, sizeof(*attr));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
* - `PTHREAD_PROCESS_SHARED`
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
|
||||
int *pshared) {
|
||||
errno_t pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = attr->_pshared;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
* - `PTHREAD_MUTEX_ERRORCHECK`
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) {
|
||||
errno_t pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) {
|
||||
*type = attr->_type;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
* Initializes mutex attr.
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
|
||||
errno_t pthread_mutexattr_init(pthread_mutexattr_t *attr) {
|
||||
*attr = (pthread_mutexattr_t){0};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raises EINVAL if `pshared` is invalid
|
||||
*/
|
||||
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
|
||||
errno_t pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
|
||||
switch (pshared) {
|
||||
case PTHREAD_PROCESS_SHARED:
|
||||
case PTHREAD_PROCESS_PRIVATE:
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raises EINVAL if `type` is invalid
|
||||
*/
|
||||
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
|
||||
errno_t pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
|
||||
switch (type) {
|
||||
case PTHREAD_MUTEX_NORMAL:
|
||||
case PTHREAD_MUTEX_RECURSIVE:
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
int _pthread_reschedule(struct PosixThread *pt) {
|
||||
errno_t _pthread_reschedule(struct PosixThread *pt) {
|
||||
int rc, e = errno;
|
||||
int policy = pt->attr.__schedpolicy;
|
||||
struct sched_param param = {pt->attr.__schedparam};
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
* @return 0 on success, or error number on failure
|
||||
* @raise EINVAL if any threads still hold the lock
|
||||
*/
|
||||
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
|
||||
errno_t pthread_rwlock_destroy(pthread_rwlock_t *rwlock) {
|
||||
memset(rwlock, -1, sizeof(*rwlock));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
* @param attr may be null
|
||||
* @return 0 on success, or error number on failure
|
||||
*/
|
||||
int pthread_rwlock_init(pthread_rwlock_t *rwlock,
|
||||
const pthread_rwlockattr_t *attr) {
|
||||
errno_t pthread_rwlock_init(pthread_rwlock_t *rwlock,
|
||||
const pthread_rwlockattr_t *attr) {
|
||||
*rwlock = (pthread_rwlock_t){0};
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Destroys read-write lock attributes.
|
||||
*
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
|
||||
errno_t pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) {
|
||||
memset(attr, -1, sizeof(*attr));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
* - `PTHREAD_PROCESS_SHARED` (unsupported)
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
|
||||
int *pshared) {
|
||||
errno_t pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = *attr;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
*
|
||||
* @return 0 on success, or error on failure
|
||||
*/
|
||||
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
|
||||
errno_t pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
|
||||
*attr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* @return 0 on success, or error on failure
|
||||
* @raises EINVAL if `pshared` is invalid
|
||||
*/
|
||||
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
|
||||
errno_t pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
|
||||
switch (pshared) {
|
||||
case PTHREAD_PROCESS_PRIVATE:
|
||||
*attr = pshared;
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*-*- 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/errno.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* Sets cancelability state.
|
||||
*
|
||||
* This function may be used to temporarily disable cancellation for the
|
||||
* calling thread, which is necessary in cases when a @cancellationpoint
|
||||
* function is invoked from an @asyncsignalsafe function.
|
||||
*
|
||||
* Cosmopolitan Libc supports the Musl Libc `PTHREAD_CANCEL_MASKED`
|
||||
* non-POSIX extension. Any thread may use this setting, in which case
|
||||
* the thread won't be abruptly destroyed upon a cancellation and have
|
||||
* its stack unwound; instead, the thread will encounter an `ECANCELED`
|
||||
* errno the next time it calls a cancellation point.
|
||||
*
|
||||
* @param state may be one of:
|
||||
* - `PTHREAD_CANCEL_ENABLE` (default)
|
||||
* - `PTHREAD_CANCEL_DISABLE`
|
||||
* - `PTHREAD_CANCEL_MASKED`
|
||||
* @param oldstate optionally receives old value
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if `state` has bad value
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int pthread_setcancelstate(int state, int *oldstate) {
|
||||
struct PosixThread *pt;
|
||||
switch (state) {
|
||||
case PTHREAD_CANCEL_ENABLE:
|
||||
case PTHREAD_CANCEL_DISABLE:
|
||||
case PTHREAD_CANCEL_MASKED:
|
||||
pt = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
if (oldstate) {
|
||||
if (pt->flags & PT_NOCANCEL) {
|
||||
*oldstate = PTHREAD_CANCEL_DISABLE;
|
||||
} else if (pt->flags & PT_MASKED) {
|
||||
*oldstate = PTHREAD_CANCEL_MASKED;
|
||||
} else {
|
||||
*oldstate = PTHREAD_CANCEL_ENABLE;
|
||||
}
|
||||
}
|
||||
pt->flags &= ~(PT_NOCANCEL | PT_MASKED);
|
||||
if (state == PTHREAD_CANCEL_MASKED) {
|
||||
pt->flags |= PT_MASKED;
|
||||
} else if (state == PTHREAD_CANCEL_DISABLE) {
|
||||
pt->flags |= PT_NOCANCEL;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@
|
|||
* @raise EINVAL if `type` has bad value
|
||||
* @see pthread_cancel() for docs
|
||||
*/
|
||||
int pthread_setcanceltype(int type, int *oldtype) {
|
||||
errno_t pthread_setcanceltype(int type, int *oldtype) {
|
||||
struct PosixThread *pt;
|
||||
switch (type) {
|
||||
case PTHREAD_CANCEL_DEFERRED:
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -28,30 +29,7 @@
|
|||
#include "libc/sysv/consts/pr.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
/**
|
||||
* Registers custom name of thread with system, e.g.
|
||||
*
|
||||
* void *worker(void *arg) {
|
||||
* pthread_setname_np(pthread_self(), "justine");
|
||||
* pause();
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* int main(int argc, char *argv[]) {
|
||||
* pthread_t id;
|
||||
* pthread_create(&id, 0, worker, 0);
|
||||
* pthread_join(id, 0);
|
||||
* }
|
||||
*
|
||||
* ProTip: The `htop` software is good at displaying thread names.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ERANGE if length of `name` exceeded system limit, in which
|
||||
* case the name may have still been set with os using truncation
|
||||
* @raise ENOSYS on MacOS, Windows, and OpenBSD
|
||||
* @see pthread_getname_np()
|
||||
*/
|
||||
errno_t pthread_setname_np(pthread_t thread, const char *name) {
|
||||
static errno_t pthread_setname_impl(pthread_t thread, const char *name) {
|
||||
char path[128], *p;
|
||||
int fd, rc, tid, len, e = errno;
|
||||
|
||||
|
@ -113,3 +91,34 @@ errno_t pthread_setname_np(pthread_t thread, const char *name) {
|
|||
return ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers custom name of thread with system, e.g.
|
||||
*
|
||||
* void *worker(void *arg) {
|
||||
* pthread_setname_np(pthread_self(), "justine");
|
||||
* pause();
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* int main(int argc, char *argv[]) {
|
||||
* pthread_t id;
|
||||
* pthread_create(&id, 0, worker, 0);
|
||||
* pthread_join(id, 0);
|
||||
* }
|
||||
*
|
||||
* ProTip: The `htop` software is good at displaying thread names.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ERANGE if length of `name` exceeded system limit, in which
|
||||
* case the name may have still been set with os using truncation
|
||||
* @raise ENOSYS on MacOS, Windows, and OpenBSD
|
||||
* @see pthread_getname_np()
|
||||
*/
|
||||
errno_t pthread_setname_np(pthread_t thread, const char *name) {
|
||||
errno_t rc;
|
||||
BLOCK_CANCELLATIONS;
|
||||
rc = pthread_setname_impl(thread, name);
|
||||
ALLOW_CANCELLATIONS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@
|
|||
* @see sched_get_priority_max()
|
||||
* @see sched_setscheduler()
|
||||
*/
|
||||
int pthread_setschedparam(pthread_t thread, int policy,
|
||||
const struct sched_param *param) {
|
||||
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;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
/**
|
||||
* Sets scheduler parameter on thread.
|
||||
*/
|
||||
int pthread_setschedprio(pthread_t thread, int prio) {
|
||||
errno_t pthread_setschedprio(pthread_t thread, int prio) {
|
||||
struct PosixThread *pt = (struct PosixThread *)thread;
|
||||
pt->attr.__schedparam = prio;
|
||||
return _pthread_reschedule(pt);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
* @return 0 on success, or errno on error
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int pthread_sigmask(int how, const sigset_t *set, sigset_t *old) {
|
||||
errno_t pthread_sigmask(int how, const sigset_t *set, sigset_t *old) {
|
||||
int rc, e = errno;
|
||||
if (!sigprocmask(how, set, old)) {
|
||||
rc = 0;
|
||||
|
|
|
@ -44,6 +44,7 @@ void _pthread_zombies_add(struct PosixThread *pt) {
|
|||
}
|
||||
|
||||
static void _pthread_zombies_collect(struct Zombie *z) {
|
||||
// TODO(jart): We need a trywait() op here to avoid cancellation.
|
||||
_pthread_wait(z->pt);
|
||||
_pthread_free(z->pt);
|
||||
free(z);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -195,6 +196,7 @@ sem_t *sem_open(const char *name, int oflag, ...) {
|
|||
if (!(path = sem_path_np(name, pathbuf, sizeof(pathbuf)))) {
|
||||
return SEM_FAILED;
|
||||
}
|
||||
BLOCK_CANCELLATIONS;
|
||||
sem_open_init();
|
||||
sem_open_lock();
|
||||
if ((s = sem_open_reopen(path))) {
|
||||
|
@ -233,6 +235,7 @@ sem_t *sem_open(const char *name, int oflag, ...) {
|
|||
sem = SEM_FAILED;
|
||||
}
|
||||
sem_open_unlock();
|
||||
ALLOW_CANCELLATIONS;
|
||||
return sem;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/limits.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/semaphore.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/futex.internal.h"
|
||||
|
||||
static void sem_delay(int n) {
|
||||
|
@ -51,11 +52,18 @@ static struct timespec *sem_timeout(struct timespec *memory,
|
|||
}
|
||||
}
|
||||
|
||||
static void sem_timedwait_cleanup(void *arg) {
|
||||
sem_t *sem = arg;
|
||||
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, -1,
|
||||
memory_order_acq_rel) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks semaphore w/ deadline.
|
||||
*
|
||||
* @param abstime is absolute deadline or null to wait forever
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||
* @raise EINTR if signal was delivered instead
|
||||
* @raise EDEADLK if deadlock was detected
|
||||
* @raise ETIMEDOUT if deadline expired
|
||||
|
@ -80,14 +88,16 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
|
|||
}
|
||||
|
||||
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, +1,
|
||||
memory_order_acquire) >= 0);
|
||||
memory_order_acq_rel) >= 0);
|
||||
pthread_cleanup_push(sem_timedwait_cleanup, sem);
|
||||
|
||||
do {
|
||||
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
|
||||
rc = nsync_futex_wait_(&sem->sem_value, v, sem->sem_pshared,
|
||||
sem_timeout(&ts, abstime));
|
||||
if (rc == -EINTR) {
|
||||
rc = eintr();
|
||||
if (rc == -EINTR || rc == -ECANCELED) {
|
||||
errno = -rc;
|
||||
rc = -1;
|
||||
} else if (rc == -EAGAIN || rc == -EWOULDBLOCK) {
|
||||
rc = 0;
|
||||
} else if (rc == -ETIMEDOUT) {
|
||||
|
@ -111,8 +121,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
|
|||
&sem->sem_value, &v, v - 1, memory_order_acquire,
|
||||
memory_order_relaxed)));
|
||||
|
||||
_unassert(atomic_fetch_add_explicit(&sem->sem_waiters, -1,
|
||||
memory_order_release) > 0);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Locks semaphore.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||
* @raise EINTR if signal was delivered instead
|
||||
* @raise EDEADLK if deadlock was detected
|
||||
* @raise EINVAL if `sem` is invalid
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/thread/wait0.internal.h"
|
||||
#include "third_party/nsync/futex.internal.h"
|
||||
|
@ -29,10 +30,16 @@
|
|||
* order to know when it's safe to free a thread's stack. This function
|
||||
* uses futexes on Linux, FreeBSD, OpenBSD, and Windows. On other
|
||||
* platforms this uses polling with exponential backoff.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ECANCELED if calling thread was cancelled in masked mode
|
||||
*/
|
||||
void _wait0(const atomic_int *ctid) {
|
||||
int x;
|
||||
errno_t _wait0(const atomic_int *ctid) {
|
||||
int x, rc;
|
||||
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
|
||||
nsync_futex_wait_(ctid, x, !IsWindows(), 0);
|
||||
if (nsync_futex_wait_(ctid, x, !IsWindows(), 0) == -ECANCELED) {
|
||||
return ECANCELED;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void _wait0(const atomic_int *) hidden;
|
||||
errno_t _wait0(const atomic_int *) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue