On 09/05/18(Wed) 14:19, Paul Irofti wrote: > > [...] > > I'd prefer if we could teach each other how stuff really work :o) > > Frankly someone else will have to enlighten me (or us) if we really > need to do this.
That's what guenther@ and visa@ did. So I believe you should move forward and commit this. Make sure the archs list contain all the new shiny FUTEX archs and don't forget the *_compat.c version :) With all of that ok mpi@. > diff --git lib/librthread/Makefile lib/librthread/Makefile > index 4c3e127491d..5dfb140290e 100644 > --- lib/librthread/Makefile > +++ lib/librthread/Makefile > @@ -30,12 +30,19 @@ SRCS= rthread.c \ > rthread_rwlock.c \ > rthread_rwlockattr.c \ > rthread_sched.c \ > - rthread_sem.c \ > rthread_sig.c \ > rthread_stack.c \ > rthread_spin_lock.c \ > sched_prio.c > > +# Architectures that implement atomics > +.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386" || \ > + ${MACHINE_ARCH} == "mips64" || ${MACHINE_ARCH} == "mips64el" > +SRCS+= rthread_sem_atomic.c > +.else > +SRCS+= rthread_sem.c > +.endif > + > SRCDIR= ${.CURDIR}/../libpthread > .include "${SRCDIR}/man/Makefile.inc" > .include <bsd.lib.mk> > diff --git lib/librthread/rthread_sem_atomic.c > lib/librthread/rthread_sem_atomic.c > new file mode 100644 > index 00000000000..c2de5d25de2 > --- /dev/null > +++ lib/librthread/rthread_sem_atomic.c > @@ -0,0 +1,429 @@ > +/* $OpenBSD$ */ > +/* > + * Copyright (c) 2004,2005,2013 Ted Unangst <t...@openbsd.org> > + * Copyright (c) 2018 Paul Irofti <piro...@openbsd.org> > + * All Rights Reserved. > + * > + * Permission to use, copy, modify, and 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 <sys/types.h> > +#include <sys/mman.h> > +#include <sys/stat.h> > +#include <sys/atomic.h> > +#include <sys/time.h> > +#include <sys/futex.h> > + > +#include <errno.h> > +#include <fcntl.h> > +#include <sha2.h> > +#include <stdarg.h> > +#include <stdlib.h> > +#include <stdio.h> > +#include <string.h> > +#include <unistd.h> > + > +#include <pthread.h> > + > +#include "rthread.h" > +#include "cancel.h" /* in libc/include */ > +#include "synch.h" > + > +#define SHARED_IDENT ((void *)-1) > + > +/* SHA256_DIGEST_STRING_LENGTH includes nul */ > +/* "/tmp/" + sha256 + ".sem" */ > +#define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4) > + > +/* long enough to be hard to guess */ > +#define SEM_RANDOM_NAME_LEN 10 > + > +/* > + * Size of memory to be mmap()'ed by named semaphores. > + * Should be >= SEM_PATH_SIZE and page-aligned. > + */ > +#define SEM_MMAP_SIZE _thread_pagesize > + > +/* > + * Internal implementation of semaphores > + */ > +int > +_sem_wait(sem_t sem, int can_eintr, const struct timespec *abstime, > + int *delayed_cancel) > +{ > + int r = 0; > + int v, ov; > + > + atomic_inc_int(&sem->waitcount); > + for (;;) { > + while ((v = sem->value) > 0) { > + ov = atomic_cas_uint(&sem->value, v, v - 1); > + if (ov == v) { > + membar_enter_after_atomic(); > + atomic_dec_int(&sem->waitcount); > + return 0; > + } > + } > + if (r) > + break; > + > + r = _twait(&sem->value, 0, CLOCK_REALTIME, abstime); > + /* ignore interruptions other than cancelation */ > + if ((r == ECANCELED && *delayed_cancel == 0) || > + (r == EINTR && !can_eintr) || r == EAGAIN) > + r = 0; > + } > + atomic_dec_int(&sem->waitcount); > + > + return r; > +} > + > +/* always increment count */ > +int > +_sem_post(sem_t sem) > +{ > + membar_exit_before_atomic(); > + atomic_inc_int(&sem->value); > + _wake(&sem->value, 1); > + return 0; > +} > + > +/* > + * exported semaphores > + */ > +int > +sem_init(sem_t *semp, int pshared, unsigned int value) > +{ > + sem_t sem; > + > + if (value > SEM_VALUE_MAX) { > + errno = EINVAL; > + return (-1); > + } > + > + if (pshared) { > + errno = EPERM; > + return (-1); > +#ifdef notyet > + char name[SEM_RANDOM_NAME_LEN]; > + sem_t *sempshared; > + int i; > + > + for (;;) { > + for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) > + name[i] = arc4random_uniform(255) + 1; > + name[SEM_RANDOM_NAME_LEN - 1] = '\0'; > + sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value); > + if (sempshared != SEM_FAILED) > + break; > + if (errno == EEXIST) > + continue; > + if (errno != EPERM) > + errno = ENOSPC; > + return (-1); > + } > + > + /* unnamed semaphore should not be opened twice */ > + if (sem_unlink(name) == -1) { > + sem_close(sempshared); > + errno = ENOSPC; > + return (-1); > + } > + > + *semp = *sempshared; > + free(sempshared); > + return (0); > +#endif > + } > + > + sem = calloc(1, sizeof(*sem)); > + if (!sem) { > + errno = ENOSPC; > + return (-1); > + } > + sem->value = value; > + *semp = sem; > + > + return (0); > +} > + > +int > +sem_destroy(sem_t *semp) > +{ > + sem_t sem; > + > + if (!_threads_ready) /* for SEM_MMAP_SIZE */ > + _rthread_init(); > + > + if (!semp || !(sem = *semp)) { > + errno = EINVAL; > + return (-1); > + } > + > + if (sem->waitcount) { > +#define MSG "sem_destroy on semaphore with waiters!\n" > + write(2, MSG, sizeof(MSG) - 1); > +#undef MSG > + errno = EBUSY; > + return (-1); > + } > + > + *semp = NULL; > + if (sem->shared) > + munmap(sem, SEM_MMAP_SIZE); > + else > + free(sem); > + > + return (0); > +} > + > +int > +sem_getvalue(sem_t *semp, int *sval) > +{ > + sem_t sem; > + > + if (!semp || !(sem = *semp)) { > + errno = EINVAL; > + return (-1); > + } > + > + *sval = sem->value; > + > + return (0); > +} > + > +int > +sem_post(sem_t *semp) > +{ > + sem_t sem; > + > + if (!semp || !(sem = *semp)) { > + errno = EINVAL; > + return (-1); > + } > + > + _sem_post(sem); > + > + return (0); > +} > + > +int > +sem_wait(sem_t *semp) > +{ > + struct tib *tib = TIB_GET(); > + pthread_t self; > + sem_t sem; > + int r; > + PREP_CANCEL_POINT(tib); > + > + if (!_threads_ready) > + _rthread_init(); > + self = tib->tib_thread; > + > + if (!semp || !(sem = *semp)) { > + errno = EINVAL; > + return (-1); > + } > + > + ENTER_DELAYED_CANCEL_POINT(tib, self); > + r = _sem_wait(sem, 1, NULL, &self->delayed_cancel); > + LEAVE_CANCEL_POINT_INNER(tib, r); > + > + if (r) { > + errno = r; > + _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, > + sem->value, errno); > + return (-1); > + } > + > + return (0); > +} > + > +int > +sem_timedwait(sem_t *semp, const struct timespec *abstime) > +{ > + struct tib *tib = TIB_GET(); > + pthread_t self; > + sem_t sem; > + int r; > + PREP_CANCEL_POINT(tib); > + > + if (!semp || !(sem = *semp) || abstime == NULL || > + abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { > + errno = EINVAL; > + return (-1); > + } > + > + if (!_threads_ready) > + _rthread_init(); > + self = tib->tib_thread; > + > + ENTER_DELAYED_CANCEL_POINT(tib, self); > + r = _sem_wait(sem, 1, abstime, &self->delayed_cancel); > + LEAVE_CANCEL_POINT_INNER(tib, r); > + > + if (r) { > + errno = r == EWOULDBLOCK ? ETIMEDOUT : r; > + _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, > + sem->value, errno); > + return (-1); > + } > + > + return (0); > +} > + > +int > +sem_trywait(sem_t *semp) > +{ > + sem_t sem; > + int v, ov; > + > + if (!semp || !(sem = *semp)) { > + errno = EINVAL; > + return (-1); > + } > + > + while ((v = sem->value) > 0) { > + ov = atomic_cas_uint(&sem->value, v, v - 1); > + if (ov == v) { > + membar_enter_after_atomic(); > + return (0); > + } > + } > + > + errno = EAGAIN; > + _rthread_debug(1, "%s: v=%d errno=%d\n", __func__, sem->value, errno); > + return (-1); > +} > + > + > +static void > +makesempath(const char *origpath, char *sempath, size_t len) > +{ > + char buf[SHA256_DIGEST_STRING_LENGTH]; > + > + SHA256Data(origpath, strlen(origpath), buf); > + snprintf(sempath, len, "/tmp/%s.sem", buf); > +} > + > +sem_t * > +sem_open(const char *name, int oflag, ...) > +{ > + char sempath[SEM_PATH_SIZE]; > + struct stat sb; > + sem_t sem, *semp; > + unsigned int value = 0; > + int created = 0, fd; > + > + if (!_threads_ready) > + _rthread_init(); > + > + if (oflag & ~(O_CREAT | O_EXCL)) { > + errno = EINVAL; > + return (SEM_FAILED); > + } > + > + if (oflag & O_CREAT) { > + va_list ap; > + va_start(ap, oflag); > + /* 3rd parameter mode is not used */ > + va_arg(ap, mode_t); > + value = va_arg(ap, unsigned); > + va_end(ap); > + > + if (value > SEM_VALUE_MAX) { > + errno = EINVAL; > + return (SEM_FAILED); > + } > + } > + > + makesempath(name, sempath, sizeof(sempath)); > + fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); > + if (fd == -1) > + return (SEM_FAILED); > + if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { > + close(fd); > + errno = EINVAL; > + return (SEM_FAILED); > + } > + if (sb.st_uid != geteuid()) { > + close(fd); > + errno = EPERM; > + return (SEM_FAILED); > + } > + if (sb.st_size != (off_t)SEM_MMAP_SIZE) { > + if (!(oflag & O_CREAT)) { > + close(fd); > + errno = EINVAL; > + return (SEM_FAILED); > + } > + if (sb.st_size != 0) { > + close(fd); > + errno = EINVAL; > + return (SEM_FAILED); > + } > + if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { > + close(fd); > + errno = EINVAL; > + return (SEM_FAILED); > + } > + > + created = 1; > + } > + sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, > + MAP_SHARED, fd, 0); > + close(fd); > + if (sem == MAP_FAILED) { > + errno = EINVAL; > + return (SEM_FAILED); > + } > + semp = malloc(sizeof(*semp)); > + if (!semp) { > + munmap(sem, SEM_MMAP_SIZE); > + errno = ENOSPC; > + return (SEM_FAILED); > + } > + if (created) { > + sem->value = value; > + sem->shared = 1; > + } > + *semp = sem; > + > + return (semp); > +} > + > +int > +sem_close(sem_t *semp) > +{ > + sem_t sem; > + > + if (!semp || !(sem = *semp) || !sem->shared) { > + errno = EINVAL; > + return (-1); > + } > + > + *semp = NULL; > + munmap(sem, SEM_MMAP_SIZE); > + free(semp); > + > + return (0); > +} > + > +int > +sem_unlink(const char *name) > +{ > + char sempath[SEM_PATH_SIZE]; > + > + makesempath(name, sempath, sizeof(sempath)); > + return (unlink(sempath)); > +} > diff --git lib/librthread/synch.h lib/librthread/synch.h > new file mode 100644 > index 00000000000..8ab379530e8 > --- /dev/null > +++ lib/librthread/synch.h > @@ -0,0 +1,61 @@ > +/* $OpenBSD: synch.h,v 1.2 2017/09/05 02:40:54 guenther Exp $ */ > +/* > + * Copyright (c) 2017 Martin Pieuchot > + * > + * Permission to use, copy, modify, and 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 <sys/atomic.h> > +#include <sys/time.h> > +#include <sys/futex.h> > + > +static inline int > +_wake(volatile uint32_t *p, int n) > +{ > + return futex(p, FUTEX_WAKE, n, NULL, NULL); > +} > + > +static inline void > +_wait(volatile uint32_t *p, int val) > +{ > + while (*p != (uint32_t)val) > + futex(p, FUTEX_WAIT, val, NULL, NULL); > +} > + > +static inline int > +_twait(volatile uint32_t *p, int val, clockid_t clockid, const struct > timespec *abs) > +{ > + struct timespec rel; > + > + if (abs == NULL) > + return futex(p, FUTEX_WAIT, val, NULL, NULL); > + > + if (abs->tv_nsec >= 1000000000 || clock_gettime(clockid, &rel)) > + return (EINVAL); > + > + rel.tv_sec = abs->tv_sec - rel.tv_sec; > + if ((rel.tv_nsec = abs->tv_nsec - rel.tv_nsec) < 0) { > + rel.tv_sec--; > + rel.tv_nsec += 1000000000; > + } > + if (rel.tv_sec < 0) > + return (ETIMEDOUT); > + > + return futex(p, FUTEX_WAIT, val, &rel, NULL); > +} > + > +static inline int > +_requeue(volatile uint32_t *p, int n, int m, volatile uint32_t *q) > +{ > + return futex(p, FUTEX_REQUEUE, n, (void *)(long)m, q); > +}