Add posix semaphores support

There's still some bugs to work out on Windows and OpenBSD.
This commit is contained in:
Justine Tunney 2022-10-14 08:25:47 -07:00
parent f52f65b2e3
commit 8111462789
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
36 changed files with 779 additions and 59 deletions

View file

@ -133,7 +133,8 @@
* @raise ENOENT if `file` doesn't exist when `O_CREAT` isn't in `flags`
* @raise ENOENT if `file` points to a string that's empty
* @raise ENOMEM if insufficient memory was available
* @raise EMFILE if `RLIMIT_NOFILE` has been reached
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
* @raise ENFILE if system-wide file limit has been reached
* @raise EOPNOTSUPP if `file` names a named socket
* @raise EFAULT if `file` points to invalid memory
* @raise ETXTBSY if writing is requested on `file` that's being executed

View file

@ -24,6 +24,7 @@
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/enum/io.h"
#include "libc/nt/enum/movefileexflags.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"

View file

@ -8,7 +8,16 @@ COSMOPOLITAN_C_START_
* @see libc/sysv/consts.sh for numbers
*/
#if defined(__GNUC__) && defined(__MNO_RED_ZONE__) && !defined(__STRICT_ANSI__)
#define errno \
(*({ \
errno_t *_ep; \
asm("call\t__errno_location" : "=a"(_ep) : /* no inputs */ : "cc"); \
_ep; \
}))
#else
#define errno (*__errno_location())
#endif
/**
* System call unavailable.
@ -687,7 +696,6 @@ extern const errno_t EXFULL;
#define EXFULL EXFULL
extern errno_t __errno;
errno_t *__errno_location(void);
COSMOPOLITAN_C_END_

View file

@ -62,11 +62,11 @@ static void _mapframe(void *p, int f) {
*/
noasan void *_extend(void *p, size_t n, void *e, int f, intptr_t h) {
char *q;
_npassert(!((uintptr_t)SHADOW(p) & (G - 1)));
_npassert((uintptr_t)p + (G << kAsanScale) <= h);
_unassert(!((uintptr_t)SHADOW(p) & (G - 1)));
_unassert((uintptr_t)p + (G << kAsanScale) <= h);
for (q = e; q < ((char *)p + n); q += 8) {
if (!((uintptr_t)q & (G - 1))) {
_npassert(q + G <= (char *)h);
_unassert(q + G <= (char *)h);
_mapframe(q, f);
if (IsAsan()) {
if (!((uintptr_t)SHADOW(q) & (G - 1))) {

View file

@ -7,7 +7,7 @@
#define _POLLTRACE 0 /* not configurable w/ flag yet */
#define _DATATRACE 1 /* not configurable w/ flag yet */
#define _STDIOTRACE 0 /* not configurable w/ flag yet */
#define _NTTRACE 0 /* not configurable w/ flag yet */
#define _NTTRACE 1 /* not configurable w/ flag yet */
#define STRACE_PROLOGUE "%rSYS %6P %'18T "

View file

@ -16,10 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/sysv/consts/clock.h"
#include "libc/thread/freebsd.internal.h"
int sys_umtx_timedwait_uint(int *p, int expect, bool pshare,
int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare,
const struct timespec *abstime) {
int op;
size_t size;

View file

@ -1,4 +1,4 @@
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_SEMAPHORE_H_
#define COSMOPOLITAN_LIBC_ISYSTEM_SEMAPHORE_H_
#include "libc/calls/semaphore.internal.h"
#include "libc/thread/semaphore.h"
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_SEMAPHORE_H_ */

View file

@ -64,7 +64,8 @@ bool32 CopyFile(const char16_t *lpExistingFileName,
bool32 MoveFile(const char16_t *lpExistingFileName,
const char16_t *lpNewFileName) paramsnonnull();
bool32 MoveFileEx(const char16_t *lpExistingFileName,
const char16_t *lpNewFileName, int dwFlags) paramsnonnull();
const char16_t *opt_lpNewFileName, int dwFlags)
paramsnonnull((1));
bool32 SetCurrentDirectory(const char16_t *lpPathName);
uint32_t GetCurrentDirectory(uint32_t nBufferLength, char16_t *out_lpBuffer);

View file

@ -46,7 +46,7 @@ struct _umtx_time {
};
int sys_umtx_op(void *, int, unsigned long, void *, void *);
int sys_umtx_timedwait_uint(int *, int, bool, const struct timespec *);
int sys_umtx_timedwait_uint(_Atomic(int) *, int, bool, const struct timespec *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -86,7 +86,7 @@ wontreturn void pthread_exit(void *rc) {
// set_tid_address() upon every program startup which isn't possible
// on non-linux platforms anyway.
atomic_store_explicit(&__get_tls()->tib_tid, 0, memory_order_release);
nsync_futex_wake_((int *)&__get_tls()->tib_tid, INT_MAX, !IsWindows());
nsync_futex_wake_(&__get_tls()->tib_tid, INT_MAX, !IsWindows());
_Exit1(0);
}
}

View file

@ -45,7 +45,7 @@
* @raise ENOSYS on MacOS, Windows, FreeBSD, and OpenBSD
*/
errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
int e, fd, rc, tid, len;
int fd, rc, tid, len, e = errno;
if (!size) return 0;
bzero(name, size);
@ -55,7 +55,6 @@ errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
// TASK_COMM_LEN is 16 on Linux so we're just being paranoid.
char buf[256] = {0};
if (tid == gettid()) {
e = errno;
if (prctl(PR_GET_NAME, buf) == -1) {
rc = errno;
errno = e;
@ -66,7 +65,6 @@ errno_t pthread_getname_np(pthread_t thread, char *name, size_t size) {
p = stpcpy(p, "/proc/self/task/");
p = FormatUint32(p, tid);
p = stpcpy(p, "/comm");
e = errno;
if ((fd = sys_open(path, O_RDONLY | O_CLOEXEC, 0)) == -1) {
rc = errno;
errno = e;

View file

@ -53,14 +53,13 @@
*/
errno_t pthread_setname_np(pthread_t thread, const char *name) {
char path[128], *p;
int e, fd, rc, tid, len;
int fd, rc, tid, len, e = errno;
tid = ((struct PosixThread *)thread)->tid;
len = strlen(name);
if (IsLinux()) {
if (tid == gettid()) {
e = errno;
if (prctl(PR_SET_NAME, name) == -1) {
rc = errno;
errno = e;
@ -71,7 +70,6 @@ errno_t pthread_setname_np(pthread_t thread, const char *name) {
p = stpcpy(p, "/proc/self/task/");
p = FormatUint32(p, tid);
p = stpcpy(p, "/comm");
e = errno;
if ((fd = sys_open(path, O_WRONLY | O_CLOEXEC, 0)) == -1) {
rc = errno;
errno = e;

View file

@ -17,10 +17,20 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/errno.h"
/**
* Examines and/or changes blocked signals on current thread.
*
* @return 0 on success, or errno on error
*/
int pthread_sigmask(int how, const sigset_t *set, sigset_t *old) {
return sigprocmask(how, set, old);
int rc, e = errno;
if (!sigprocmask(how, set, old)) {
rc = 0;
} else {
rc = errno;
errno = e;
}
return rc;
}

32
libc/thread/sem_close.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- 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/runtime/runtime.h"
#include "libc/thread/semaphore.h"
/**
* Closes named semaphore.
*
* @param sem was created with sem_open()
* @return 0 on success, or -1 w/ errno
*/
int sem_close(sem_t *sem) {
_npassert(!munmap(sem, FRAMESIZE));
return 0;
}

32
libc/thread/sem_destroy.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- 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/intrin/atomic.h"
#include "libc/limits.h"
#include "libc/thread/semaphore.h"
/**
* Destroys unnamed semaphore.
*
* @param sem was created by sem_init()
* @return 0 on success, or -1 w/ errno
*/
int sem_destroy(sem_t *sem) {
atomic_store_explicit(&sem->sem_value, INT_MIN, memory_order_relaxed);
return 0;
}

View file

@ -0,0 +1,32 @@
/*-*- 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/intrin/atomic.h"
#include "libc/thread/semaphore.h"
/**
* Destroys unnamed semaphore.
*
* @param sem was created by sem_init()
* @param sval receives output value
* @return 0 on success, or -1 w/ errno
*/
int sem_getvalue(sem_t *sem, int *sval) {
*sval = atomic_load_explicit(&sem->sem_value, memory_order_relaxed);
return 0;
}

39
libc/thread/sem_init.c Normal file
View file

@ -0,0 +1,39 @@
/*-*- 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/intrin/atomic.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "third_party/nsync/mu_semaphore.h"
/**
* Initializes unnamed semaphore.
*
* @param sem should make its way to sem_destroy() if this succeeds
* @param pshared if semaphore may be shared between processes
* @param value is initial count of semaphore
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `value` exceeds `SEM_VALUE_MAX`
*/
int sem_init(sem_t *sem, int pshared, unsigned value) {
if (value > SEM_VALUE_MAX) return einval();
atomic_store_explicit(&sem->sem_value, value, memory_order_relaxed);
sem->sem_pshared = pshared;
return 0;
}

34
libc/thread/sem_name.c Normal file
View file

@ -0,0 +1,34 @@
/*-*- 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/runtime/runtime.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/thread/semaphore.internal.h"
#include "libc/thread/thread.h"
const char *__sem_name(const char *name, char path[hasatleast PATH_MAX]) {
if (_isabspath(name)) {
return name;
} else {
strlcpy(path, kTmpPath, PATH_MAX);
strlcat(path, ".sem-", PATH_MAX);
strlcat(path, name, PATH_MAX);
return path;
}
}

72
libc/thread/sem_open.c Normal file
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/calls.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/semaphore.internal.h"
/**
* Initializes and opens named semaphore.
*
* @param name can be absolute path or should be component w/o slashes
* @param oflga can have `O_CREAT` and/or `O_EXCL`
* @return semaphore object which needs sem_close(), or SEM_FAILED w/ errno
* @raise ENOTDIR if a directory component in `name` exists as non-directory
* @raise ENAMETOOLONG if symlink-resolved `name` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `name` exists longer than `NAME_MAX`
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `name` is a symbolic link
* @raise ENOSPC if file system is full when `name` would be `O_CREAT`ed
* @raise ELOOP if a loop was detected resolving components of `name`
* @raise EEXIST if `O_CREAT|O_EXCL` is used and semaphore exists
* @raise EACCES if we didn't have permission to create semaphore
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
* @raise ENFILE if system-wide file limit has been reached
* @raise EINTR if signal was delivered instead
*/
sem_t *sem_open(const char *name, int oflag, ...) {
int fd;
sem_t *sem;
va_list va;
unsigned mode;
char path[PATH_MAX];
va_start(va, oflag);
mode = va_arg(va, unsigned);
va_end(va);
oflag |= O_RDWR | O_CLOEXEC;
if ((fd = openat(AT_FDCWD, __sem_name(name, path), oflag, mode)) == -1) {
return SEM_FAILED;
}
if (ftruncate(fd, sizeof(sem_t)) == -1) {
_npassert(!close(fd));
return SEM_FAILED;
}
sem = mmap(0, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (sem != MAP_FAILED) sem->sem_pshared = true;
_npassert(!close(fd));
return sem;
}

44
libc/thread/sem_post.c Normal file
View file

@ -0,0 +1,44 @@
/*-*- 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/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "third_party/nsync/futex.internal.h"
/**
* Unlocks semaphore.
*
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `sem` isn't valid
*/
int sem_post(sem_t *sem) {
int rc;
int old = atomic_fetch_add_explicit(&sem->sem_value, 1, memory_order_relaxed);
if (old >= 0) {
_npassert(nsync_futex_wake_(&sem->sem_value, 1, sem->sem_pshared) >= 0);
rc = 0;
} else {
rc = einval();
}
STRACE("sem_post(%p) → %d% m", sem, rc);
return rc;
}

113
libc/thread/sem_timedwait.c Normal file
View file

@ -0,0 +1,113 @@
/*-*- 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/calls/struct/timespec.internal.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/semaphore.internal.h"
#include "third_party/nsync/futex.internal.h"
static void sem_delay(int n) {
volatile int i;
for (i = 0; i != 1 << n; i++) donothing;
}
// TODO(jart): This should be abstracted by polyfill.
static struct timespec *sem_timeout(struct timespec *memory,
const struct timespec *abstime) {
struct timespec now;
if (!abstime) {
return 0;
} else if (FUTEX_TIMEOUT_IS_ABSOLUTE) {
*memory = *abstime;
return memory;
} else {
now = _timespec_real();
if (_timespec_cmp(now, *abstime) > 0) {
*memory = (struct timespec){0};
} else {
*memory = _timespec_sub(*abstime, now);
}
return memory;
}
}
/**
* Locks semaphore w/ deadline.
*
* @param abstime is absolute deadline or null to wait forever
* @return 0 on success, or -1 w/ errno
* @raise EINTR if signal was delivered instead
* @raise EDEADLK if deadlock was detected
* @raise ETIMEDOUT if deadline expired
* @raise EINVAL if `sem` is invalid
*/
int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
int e, i, v, rc;
struct timespec ts;
e = errno;
for (i = 0; i < 7; ++i) {
rc = sem_trywait(sem);
if (!rc) {
return rc;
} else if (errno == EAGAIN) {
errno = e;
sem_delay(i);
} else {
return rc;
}
}
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();
} else if (rc == -EAGAIN || rc == -EWOULDBLOCK) {
rc = 0;
} else if (rc == -ETIMEDOUT) {
_npassert(abstime);
if (_timespec_cmp(*abstime, _timespec_real()) <= 0) {
rc = etimedout();
} else {
rc = 0;
}
} else {
_npassert(!rc);
rc = 0;
}
} else if (v > 0) {
rc = 0;
} else {
rc = einval();
}
} while (!rc && (!v || !atomic_compare_exchange_weak_explicit(
&sem->sem_value, &v, v - 1, memory_order_acquire,
memory_order_relaxed)));
STRACE("sem_timedwait(%p, %s) → %d% m", sem, DescribeTimespec(0, abstime),
rc);
return rc;
}

42
libc/thread/sem_trywait.c Normal file
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/assert.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
/**
* Locks semaphore only if it's currently not locked.
*
* @return 0 on success, or -1 w/ errno
* @raise EAGAIN if semaphore is locked
* @raise EDEADLK if deadlock was detected
* @raise EINVAL if `sem` is invalid
*/
int sem_trywait(sem_t *sem) {
int v;
v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed);
do {
if (!v) return eagain();
if (v < 0) return einval();
} while (!atomic_compare_exchange_weak_explicit(
&sem->sem_value, &v, v - 1, memory_order_acquire, memory_order_relaxed));
return 0;
}

35
libc/thread/sem_unlink.c Normal file
View file

@ -0,0 +1,35 @@
/*-*- 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/calls/calls.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/semaphore.internal.h"
/**
* Removes named semaphore.
*
* @param name can be absolute path or should be component w/o slashes
* @return 0 on success, or -1 w/ errno
* @raise EPERM if pledge() is in play w/o `cpath` promise
* @raise ENOENT if named semaphore doesn't exist
* @raise EACCES if permission is denied
*/
int sem_unlink(const char *name) {
char path[PATH_MAX];
return unlink(__sem_name(name, path));
}

31
libc/thread/sem_wait.c Normal file
View file

@ -0,0 +1,31 @@
/*-*- 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/semaphore.h"
/**
* Locks semaphore.
*
* @return 0 on success, or -1 w/ errno
* @raise EINTR if signal was delivered instead
* @raise EDEADLK if deadlock was detected
* @raise EINVAL if `sem` is invalid
*/
int sem_wait(sem_t *sem) {
return sem_timedwait(sem, 0);
}

View file

@ -7,19 +7,25 @@ COSMOPOLITAN_C_START_
#define SEM_FAILED ((sem_t *)0)
typedef struct {
volatile int __val[4 * sizeof(long) / sizeof(int)];
union {
struct {
_Atomic(int) sem_value;
bool sem_pshared;
};
void *sem_space[32];
};
} sem_t;
int sem_close(sem_t *);
int sem_destroy(sem_t *);
int sem_getvalue(sem_t *, int *);
int sem_init(sem_t *, int, unsigned);
sem_t *sem_open(const char *, int, ...);
int sem_destroy(sem_t *);
int sem_post(sem_t *);
int sem_timedwait(sem_t *, const struct timespec *);
int sem_trywait(sem_t *);
int sem_unlink(const char *);
int sem_wait(sem_t *);
int sem_trywait(sem_t *);
int sem_timedwait(sem_t *, const struct timespec *);
int sem_getvalue(sem_t *, int *);
sem_t *sem_open(const char *, int, ...);
int sem_close(sem_t *);
int sem_unlink(const char *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -0,0 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_SEMAPHORE_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_SEMAPHORE_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
const char *__sem_name(const char *, char[hasatleast PATH_MAX]) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_THREAD_SEMAPHORE_INTERNAL_H_ */

View file

@ -200,8 +200,6 @@ void _pthread_cleanup_push(struct _pthread_cleanup_buffer *, void (*)(void *),
_pthread_cleanup_pop(&_buffer, (execute)); \
}
#define pthread_spin_init(pSpin, multiprocess) ((pSpin)->_lock = 0, 0)
#define pthread_spin_destroy(pSpin) ((pSpin)->_lock = -1, 0)
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 && \
!defined(__STRICT_ANSI__)
extern const errno_t EBUSY;
@ -222,6 +220,10 @@ extern const errno_t EBUSY;
pthread_spinlock_t *_s = pSpin; \
__atomic_test_and_set(&_s->_lock, __ATOMIC_ACQUIRE) ? EBUSY : 0; \
})
#define pthread_spin_init(pSpin, multiprocess) \
(__atomic_store_n(&(pSpin)->_lock, 0, __ATOMIC_RELAXED), 0)
#define pthread_spin_destroy(pSpin) \
(__atomic_store_n(&(pSpin)->_lock, -1, __ATOMIC_RELAXED), 0)
#endif /* GCC 4.7+ */
COSMOPOLITAN_C_END_

View file

@ -33,6 +33,6 @@
void _wait0(const atomic_int *ctid) {
int x;
while ((x = atomic_load_explicit(ctid, memory_order_relaxed))) {
nsync_futex_wait_((int *)ctid, x, !IsWindows(), 0);
nsync_futex_wait_(ctid, x, !IsWindows(), 0);
}
}

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/limits.h"
@ -29,6 +30,7 @@
#include "libc/stdio/rand.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
@ -136,4 +138,7 @@ TEST(popen, torture) {
for (i = 0; i < n; ++i) {
ASSERT_EQ(0, pthread_join(t[i], 0));
}
for (i = 3; i < 16; ++i) {
ASSERT_SYS(EBADF, -1, fcntl(3, F_GETFL));
}
}

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/dce.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/temp.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/thread.h"
pthread_barrier_t barrier;
char testlib_enable_tmp_setup_teardown;
void *Worker(void *arg) {
sem_t *s[2];
struct timespec ts;
ASSERT_NE(SEM_FAILED, (s[0] = sem_open("fooz", O_CREAT, 0644)));
ASSERT_NE(SEM_FAILED, (s[1] = sem_open("barz", O_CREAT, 0644)));
if (pthread_barrier_wait(&barrier) == PTHREAD_BARRIER_SERIAL_THREAD) {
if (!IsWindows()) { // :'(
ASSERT_SYS(0, 0, sem_unlink("fooz"));
ASSERT_SYS(0, 0, sem_unlink("barz"));
}
}
ASSERT_SYS(0, 0, clock_gettime(CLOCK_REALTIME, &ts));
ts.tv_sec += 1;
ASSERT_SYS(0, 0, sem_post(s[0]));
ASSERT_SYS(0, 0, sem_timedwait(s[1], &ts));
ASSERT_SYS(0, 0, sem_close(s[1]));
ASSERT_SYS(0, 0, sem_close(s[0]));
return 0;
}
TEST(sem_open, test) {
if (IsWindows()) return; // TODO(jart): fix me
sem_t *s[2];
int i, r, n = 4;
pthread_t *t = _gc(malloc(sizeof(pthread_t) * n));
ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n));
ASSERT_NE(SEM_FAILED, (s[0] = sem_open("fooz", O_CREAT, 0644)));
ASSERT_NE(SEM_FAILED, (s[1] = sem_open("barz", O_CREAT, 0644)));
for (i = 0; i < n; ++i) ASSERT_EQ(0, pthread_create(t + i, 0, Worker, 0));
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, sem_wait(s[0]));
ASSERT_SYS(0, 0, sem_getvalue(s[0], &r));
ASSERT_EQ(0, r);
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, sem_post(s[1]));
for (i = 0; i < n; ++i) ASSERT_EQ(0, pthread_join(t[i], 0));
ASSERT_SYS(0, 0, sem_getvalue(s[1], &r));
ASSERT_EQ(0, r);
ASSERT_SYS(0, 0, sem_close(s[1]));
ASSERT_SYS(0, 0, sem_close(s[0]));
ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
}

View file

@ -0,0 +1,97 @@
/*-*- 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/calls/calls.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/thread.h"
void *Worker(void *arg) {
int rc;
sem_t **s = arg;
struct timespec ts;
ASSERT_SYS(0, 0, clock_gettime(CLOCK_REALTIME, &ts));
ts.tv_sec += 1;
ASSERT_SYS(0, 0, sem_post(s[0]));
ASSERT_SYS(0, 0, sem_timedwait(s[1], &ts));
return 0;
}
TEST(sem_timedwait, threads) {
int i, r, n = 4;
sem_t sm[2], *s[2] = {sm, sm + 1};
pthread_t *t = _gc(malloc(sizeof(pthread_t) * n));
ASSERT_SYS(0, 0, sem_init(s[0], 0, 0));
ASSERT_SYS(0, 0, sem_init(s[1], 0, 0));
for (i = 0; i < n; ++i) ASSERT_EQ(0, pthread_create(t + i, 0, Worker, s));
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, sem_wait(s[0]));
ASSERT_SYS(0, 0, sem_getvalue(s[0], &r));
ASSERT_EQ(0, r);
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, sem_post(s[1]));
for (i = 0; i < n; ++i) ASSERT_EQ(0, pthread_join(t[i], 0));
ASSERT_SYS(0, 0, sem_getvalue(s[1], &r));
ASSERT_EQ(0, r);
ASSERT_SYS(0, 0, sem_destroy(s[1]));
ASSERT_SYS(0, 0, sem_destroy(s[0]));
}
TEST(sem_timedwait, processes) {
if (IsOpenbsd()) return; // TODO(jart): fix me
int i, r, rc, n = 4, pshared = 1;
sem_t *sm = _mapshared(FRAMESIZE), *s[2] = {sm, sm + 1};
ASSERT_SYS(0, 0, sem_init(s[0], pshared, 0));
ASSERT_SYS(0, 0, sem_init(s[1], pshared, 0));
for (i = 0; i < n; ++i) {
ASSERT_NE(-1, (rc = fork()));
if (!rc) Worker(s), _Exit(0);
}
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, sem_wait(s[0]));
ASSERT_SYS(0, 0, sem_getvalue(s[0], &r));
ASSERT_EQ(0, r);
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, sem_post(s[1]));
for (;;) {
int ws, pid, e = errno;
if ((pid = waitpid(0, &ws, 0)) != -1) {
if (WIFSIGNALED(ws)) {
kprintf("process %d terminated with %G\n", pid, WTERMSIG(ws));
testlib_incrementfailed();
} else if (WEXITSTATUS(ws)) {
kprintf("process %d exited with %d\n", pid, WEXITSTATUS(ws));
testlib_incrementfailed();
}
} else {
ASSERT_EQ(ECHILD, errno);
errno = e;
break;
}
}
ASSERT_SYS(0, 0, sem_getvalue(s[1], &r));
ASSERT_EQ(0, r);
ASSERT_SYS(0, 0, sem_destroy(s[1]));
ASSERT_SYS(0, 0, sem_destroy(s[0]));
ASSERT_SYS(0, 0, munmap(sm, FRAMESIZE));
}

View file

@ -29,12 +29,13 @@ TEST_LIBC_THREAD_DIRECTDEPS = \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_TESTLIB \
LIBC_THREAD \
LIBC_TIME \
LIBC_TESTLIB \
THIRD_PARTY_NSYNC \
THIRD_PARTY_NSYNC_MEM

View file

@ -2856,8 +2856,8 @@ static int LuaUnixMemoryWait(lua_State *L) {
}
deadline = &ts;
}
rc = nsync_futex_wait_((int *)GetWord(L), expect, PTHREAD_PROCESS_SHARED,
deadline);
rc = nsync_futex_wait_((atomic_int *)GetWord(L), expect,
PTHREAD_PROCESS_SHARED, deadline);
if (rc < 0) errno = -rc, rc = -1;
return SysretInteger(L, "futex_wait", olderr, rc);
}
@ -2867,7 +2867,8 @@ static int LuaUnixMemoryWait(lua_State *L) {
static int LuaUnixMemoryWake(lua_State *L) {
int count, woken;
count = luaL_optinteger(L, 3, INT_MAX);
woken = nsync_futex_wake_((int *)GetWord(L), count, PTHREAD_PROCESS_SHARED);
woken = nsync_futex_wake_((atomic_int *)GetWord(L), count,
PTHREAD_PROCESS_SHARED);
_npassert(woken >= 0);
return ReturnInteger(L, woken);
}

View file

@ -39,6 +39,7 @@
#include "libc/thread/tls.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/futex.internal.h"
#include "third_party/nsync/time.h"
// clang-format off
@ -46,7 +47,7 @@
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
int _futex (int *, int, int, const struct timespec *, int *, int);
int _futex (atomic_int *, int, int, const struct timespec *, int *, int);
static int FUTEX_WAIT_;
static int FUTEX_PRIVATE_FLAG_;
@ -54,7 +55,7 @@ static bool FUTEX_IS_SUPPORTED;
bool FUTEX_TIMEOUT_IS_ABSOLUTE;
__attribute__((__constructor__)) static void nsync_futex_init_ (void) {
int x = 0;
atomic_int x;
FUTEX_WAIT_ = FUTEX_WAIT;
@ -87,6 +88,7 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
// configuring any time synchronization mechanism (like ntp) to
// adjust for leap seconds by adjusting the rate, rather than
// with a backwards step.
atomic_store_explicit (&x, 0, memory_order_relaxed);
if (IsLinux () &&
_futex (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME,
1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
@ -109,13 +111,11 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
}
}
static int nsync_futex_polyfill_ (int *p, int expect, struct timespec *timeout) {
static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *timeout) {
int64_t nanos, maxnanos;
nsync_atomic_uint32_ *w;
struct timespec ts, deadline;
w = (nsync_atomic_uint32_ *)p;
if (ATM_LOAD (p) != expect) {
if (atomic_load_explicit (w, memory_order_relaxed) != expect) {
return -EAGAIN;
}
@ -131,7 +131,7 @@ static int nsync_futex_polyfill_ (int *p, int expect, struct timespec *timeout)
nanos = 100;
maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000;
while (nsync_time_cmp (deadline, ts) > 0) {
if (ATM_LOAD (p) != expect) {
if (atomic_load_explicit (w, memory_order_relaxed) != expect) {
return 0;
}
ts = nsync_time_add (ts, _timespec_fromnanos (nanos));
@ -152,7 +152,7 @@ static int nsync_futex_polyfill_ (int *p, int expect, struct timespec *timeout)
return -ETIMEDOUT;
}
int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout) {
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) {
uint32_t ms;
int rc, op, fop;
@ -175,7 +175,7 @@ int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout
} else {
ms = -1;
}
if (WaitOnAddress (p, &expect, sizeof(int), ms)) {
if (WaitOnAddress (w, &expect, sizeof(int), ms)) {
rc = 0;
} else {
rc = -GetLastError ();
@ -183,9 +183,9 @@ int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout
}
} else if (IsFreebsd ()) {
rc = sys_umtx_timedwait_uint (
p, expect, pshare, timeout);
w, expect, pshare, timeout);
} else {
rc = _futex (p, op, expect, timeout, 0,
rc = _futex (w, op, expect, timeout, 0,
FUTEX_WAIT_BITS_);
if (IsOpenbsd() && rc > 0) {
rc = -rc;
@ -194,19 +194,19 @@ int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout
} else {
Polyfill:
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
rc = nsync_futex_polyfill_ (p, expect, timeout);
rc = nsync_futex_polyfill_ (w, expect, timeout);
__get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
}
STRACE ("futex(%t, %s, %#x, %s) → %s",
p, DescribeFutexOp (op), expect,
STRACE ("futex(%t [%d], %s, %#x, %s) → %s",
w, *w, DescribeFutexOp (op), expect,
DescribeTimespec (0, timeout),
DescribeErrnoResult (rc));
return rc;
}
int nsync_futex_wake_ (int *p, int count, char pshare) {
int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
int e, rc, op, fop;
int wake (void *, int, int) asm ("_futex");
@ -223,9 +223,9 @@ int nsync_futex_wake_ (int *p, int count, char pshare) {
goto Polyfill;
}
if (count == 1) {
WakeByAddressSingle (p);
WakeByAddressSingle (w);
} else {
WakeByAddressAll (p);
WakeByAddressAll (w);
}
rc = 0;
} else if (IsFreebsd ()) {
@ -234,9 +234,9 @@ int nsync_futex_wake_ (int *p, int count, char pshare) {
} else {
fop = UMTX_OP_WAKE_PRIVATE;
}
rc = sys_umtx_op (p, fop, count, 0, 0);
rc = sys_umtx_op (w, fop, count, 0, 0);
} else {
rc = wake (p, op, count);
rc = wake (w, op, count);
}
} else {
Polyfill:
@ -244,8 +244,8 @@ int nsync_futex_wake_ (int *p, int count, char pshare) {
rc = 0;
}
STRACE ("futex(%t, %s, %d) → %s",
p, DescribeFutexOp(op),
STRACE ("futex(%t [%d], %s, %d) → %s",
w, *w, DescribeFutexOp(op),
count, DescribeErrnoResult(rc));
return rc;

View file

@ -7,8 +7,8 @@ COSMOPOLITAN_C_START_
extern bool FUTEX_TIMEOUT_IS_ABSOLUTE;
int nsync_futex_wake_(int *, int, char);
int nsync_futex_wait_(int *, int, char, struct timespec *);
int nsync_futex_wake_(_Atomic(int) *, int, char);
int nsync_futex_wait_(_Atomic(int) *, int, char, struct timespec *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -57,9 +57,10 @@ void nsync_mu_semaphore_p (nsync_semaphore *s) {
do {
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
if (i == 0) {
int futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, NULL);
int futex_result = nsync_futex_wait_ ((atomic_int *)&f->i, i, PTHREAD_PROCESS_PRIVATE, NULL);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -EAGAIN ||
futex_result == -EWOULDBLOCK);
}
} while (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i-1));
@ -98,9 +99,10 @@ int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadl
}
ts = &ts_buf;
}
futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, ts);
futex_result = nsync_futex_wait_ ((atomic_int *)&f->i, i, PTHREAD_PROCESS_PRIVATE, ts);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -EAGAIN ||
futex_result == -ETIMEDOUT ||
futex_result == -EWOULDBLOCK);
/* Some systems don't wait as long as they are told. */
@ -120,5 +122,5 @@ void nsync_mu_semaphore_v (nsync_semaphore *s) {
do {
old_value = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
} while (!ATM_CAS_REL ((nsync_atomic_uint32_ *) &f->i, old_value, old_value+1));
ASSERT (nsync_futex_wake_ (&f->i, 1, PTHREAD_PROCESS_PRIVATE) >= 0);
ASSERT (nsync_futex_wake_ ((atomic_int *)&f->i, 1, PTHREAD_PROCESS_PRIVATE) >= 0);
}