mirror of
https://abf.rosa.ru/djam/glibc40.git
synced 2025-02-23 15:52:52 +00:00
382 lines
13 KiB
Diff
382 lines
13 KiB
Diff
From 4483f2500825a84382c2a6a9ac60fc77954533d7 Mon Sep 17 00:00:00 2001
|
|
From: Mike Lothian <mike@fireburn.co.uk>
|
|
Date: Tue, 4 Feb 2020 11:04:42 +0000
|
|
Subject: [PATCH] Implement pthread_mutex_lock_any() and
|
|
pthread_mutex_timedlock_any().
|
|
|
|
On newer Linux kernels, futex supports a WAIT_MULTIPLE operation that can be
|
|
used to implement new locking functionality that allows a given application
|
|
thread to sleep while waiting for one of multiple given locks to become
|
|
available.
|
|
|
|
Patch-by: Pierre-Loup A. Griffais <pgriffais@valvesoftware.com>
|
|
Rebased-by: Mike Lothian <mike@fireburn.co.uk>
|
|
---
|
|
nptl/Makefile | 1 +
|
|
nptl/Versions | 2 +
|
|
nptl/pthreadP.h | 5 +
|
|
nptl/pthread_mutex_lock_any.c | 37 ++++
|
|
nptl/pthread_mutex_timedlock_any.c | 193 ++++++++++++++++++
|
|
sysdeps/nptl/lowlevellock-futex.h | 14 ++
|
|
sysdeps/nptl/pthread.h | 10 +
|
|
.../sysv/linux/x86_64/64/libpthread.abilist | 1 +
|
|
8 files changed, 263 insertions(+)
|
|
create mode 100644 nptl/pthread_mutex_lock_any.c
|
|
create mode 100644 nptl/pthread_mutex_timedlock_any.c
|
|
|
|
diff --git a/nptl/Makefile b/nptl/Makefile
|
|
index 89569c4f46..11368c93cb 100644
|
|
--- a/nptl/Makefile
|
|
+++ b/nptl/Makefile
|
|
@@ -97,6 +97,7 @@ libpthread-routines = nptl-init nptlfreeres vars events version pt-interp \
|
|
pthread_attr_getstack pthread_attr_setstack \
|
|
pthread_mutex_init pthread_mutex_destroy \
|
|
pthread_mutex_lock pthread_mutex_trylock \
|
|
+ pthread_mutex_lock_any pthread_mutex_timedlock_any \
|
|
pthread_mutex_timedlock pthread_mutex_unlock \
|
|
pthread_mutex_cond_lock \
|
|
pthread_mutexattr_init pthread_mutexattr_destroy \
|
|
diff --git a/nptl/Versions b/nptl/Versions
|
|
index aed118e717..df4a8313e2 100644
|
|
--- a/nptl/Versions
|
|
+++ b/nptl/Versions
|
|
@@ -285,6 +285,8 @@ libpthread {
|
|
mtx_init; mtx_lock; mtx_timedlock; mtx_trylock; mtx_unlock; mtx_destroy;
|
|
call_once; cnd_broadcast; cnd_destroy; cnd_init; cnd_signal;
|
|
cnd_timedwait; cnd_wait; tss_create; tss_delete; tss_get; tss_set;
|
|
+
|
|
+ pthread_mutex_lock_any; pthread_mutex_timedlock_any;
|
|
}
|
|
|
|
GLIBC_2.30 {
|
|
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
|
|
index 6f94d6be31..87f4afff30 100644
|
|
--- a/nptl/pthreadP.h
|
|
+++ b/nptl/pthreadP.h
|
|
@@ -395,6 +395,11 @@ extern int __pthread_mutex_trylock (pthread_mutex_t *_mutex);
|
|
extern int __pthread_mutex_lock (pthread_mutex_t *__mutex);
|
|
extern int __pthread_mutex_timedlock (pthread_mutex_t *__mutex,
|
|
const struct timespec *__abstime);
|
|
+extern int __pthread_mutex_lock_any (pthread_mutex_t *__mutex, int mutexcount,
|
|
+ int *outlocked);
|
|
+extern int __pthread_mutex_timedlock_any (pthread_mutex_t *__mutex, int count,
|
|
+ const struct timespec *__abstime,
|
|
+ int *outlocked);
|
|
extern int __pthread_mutex_cond_lock (pthread_mutex_t *__mutex)
|
|
attribute_hidden;
|
|
extern void __pthread_mutex_cond_lock_adjust (pthread_mutex_t *__mutex)
|
|
diff --git a/nptl/pthread_mutex_lock_any.c b/nptl/pthread_mutex_lock_any.c
|
|
new file mode 100644
|
|
index 0000000000..485c213a17
|
|
--- /dev/null
|
|
+++ b/nptl/pthread_mutex_lock_any.c
|
|
@@ -0,0 +1,37 @@
|
|
+/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
|
|
+ This file is part of the GNU C Library.
|
|
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
|
|
+
|
|
+ The GNU C Library is free software; you can redistribute it and/or
|
|
+ modify it under the terms of the GNU Lesser General Public
|
|
+ License as published by the Free Software Foundation; either
|
|
+ version 2.1 of the License, or (at your option) any later version.
|
|
+
|
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public
|
|
+ License along with the GNU C Library; if not, see
|
|
+ <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <errno.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/param.h>
|
|
+#include <not-cancel.h>
|
|
+#include "pthreadP.h"
|
|
+#include <atomic.h>
|
|
+#include <lowlevellock.h>
|
|
+#include <stap-probe.h>
|
|
+
|
|
+int
|
|
+__pthread_mutex_lock_any (pthread_mutex_t *mutexlist, int mutexcount,
|
|
+ int *outlocked)
|
|
+{
|
|
+ return __pthread_mutex_timedlock_any(mutexlist, mutexcount, NULL, outlocked);
|
|
+}
|
|
+
|
|
+weak_alias (__pthread_mutex_lock_any, pthread_mutex_lock_any)
|
|
diff --git a/nptl/pthread_mutex_timedlock_any.c b/nptl/pthread_mutex_timedlock_any.c
|
|
new file mode 100644
|
|
index 0000000000..a95687ce8e
|
|
--- /dev/null
|
|
+++ b/nptl/pthread_mutex_timedlock_any.c
|
|
@@ -0,0 +1,193 @@
|
|
+/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
|
|
+ This file is part of the GNU C Library.
|
|
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
|
|
+
|
|
+ The GNU C Library is free software; you can redistribute it and/or
|
|
+ modify it under the terms of the GNU Lesser General Public
|
|
+ License as published by the Free Software Foundation; either
|
|
+ version 2.1 of the License, or (at your option) any later version.
|
|
+
|
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public
|
|
+ License along with the GNU C Library; if not, see
|
|
+ <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <errno.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/param.h>
|
|
+#include <not-cancel.h>
|
|
+#include "pthreadP.h"
|
|
+#include <atomic.h>
|
|
+#include <lowlevellock.h>
|
|
+#include <stap-probe.h>
|
|
+
|
|
+/* TODO: this probably comes from a kernel header when upstream? */
|
|
+struct futex_wait_block {
|
|
+ int *uaddr;
|
|
+ int val;
|
|
+ int bitset;
|
|
+} __attribute__((packed));
|
|
+
|
|
+int
|
|
+__pthread_mutex_timedlock_any (pthread_mutex_t *mutexlist, int mutexcount,
|
|
+ const struct timespec *abstime, int *outlocked)
|
|
+{
|
|
+ /* This requires futex support */
|
|
+#ifndef __NR_futex
|
|
+ return ENOTSUP;
|
|
+#endif
|
|
+
|
|
+ if (mutexlist == NULL)
|
|
+ {
|
|
+ /* User is asking us if kernel supports the feature. */
|
|
+
|
|
+ /* TODO: how does one check if supported?
|
|
+ * I was thinking of trying the ioctl once and then returning the static
|
|
+ * cached value, is that OK?
|
|
+ */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (mutexlist != NULL && mutexcount <= 0)
|
|
+ return EINVAL;
|
|
+
|
|
+ if (outlocked == NULL)
|
|
+ return EINVAL;
|
|
+
|
|
+ int type = PTHREAD_MUTEX_TYPE (mutexlist);
|
|
+
|
|
+ for (int i = 1; i < mutexcount; i++)
|
|
+ {
|
|
+ /* Types have to match, since the PRIVATE flag is OP-global. */
|
|
+ if (PTHREAD_MUTEX_TYPE (&mutexlist[i]) != type)
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ int kind = type & PTHREAD_MUTEX_KIND_MASK_NP;
|
|
+
|
|
+ /* TODO: implement recursive, errorcheck and adaptive. */
|
|
+ if (kind != PTHREAD_MUTEX_NORMAL)
|
|
+ return EINVAL;
|
|
+
|
|
+ /* TODO: implement robust. */
|
|
+ if (type & PTHREAD_MUTEX_ROBUST_NORMAL_NP)
|
|
+ return EINVAL;
|
|
+
|
|
+ /* TODO: implement PI. */
|
|
+ if (type & PTHREAD_MUTEX_PRIO_INHERIT_NP)
|
|
+ return EINVAL;
|
|
+
|
|
+ /* TODO: implement PP. */
|
|
+ if (type & PTHREAD_MUTEX_PRIO_PROTECT_NP)
|
|
+ return EINVAL;
|
|
+
|
|
+ pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
|
|
+ int result;
|
|
+
|
|
+ result = -1;
|
|
+
|
|
+ for (int i = 0; i < mutexcount; i++)
|
|
+ {
|
|
+ if (__lll_trylock (&mutexlist[i].__data.__lock) == 0)
|
|
+ {
|
|
+ result = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ while (result == -1)
|
|
+ {
|
|
+ for (int i = 0; i < mutexcount; i++)
|
|
+ {
|
|
+ int oldval = atomic_exchange_acq (&mutexlist[i].__data.__lock, 2);
|
|
+
|
|
+ if (oldval == 0)
|
|
+ {
|
|
+ result = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (result == -1)
|
|
+ {
|
|
+ /* Couldn't get one of the locks immediately, we have to sleep now. */
|
|
+ struct timespec *timeout = NULL;
|
|
+ struct timespec rt;
|
|
+
|
|
+ if (abstime != NULL)
|
|
+ {
|
|
+ /* Reject invalid timeouts. */
|
|
+ if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
|
+ return EINVAL;
|
|
+
|
|
+ struct timeval tv;
|
|
+
|
|
+ /* Get the current time. */
|
|
+ (void) __gettimeofday (&tv, NULL);
|
|
+
|
|
+ /* Compute relative timeout. */
|
|
+ rt.tv_sec = abstime->tv_sec - tv.tv_sec;
|
|
+ rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000;
|
|
+ if (rt.tv_nsec < 0)
|
|
+ {
|
|
+ rt.tv_nsec += 1000000000;
|
|
+ --rt.tv_sec;
|
|
+ }
|
|
+
|
|
+ if (rt.tv_sec < 0)
|
|
+ return ETIMEDOUT;
|
|
+
|
|
+ timeout = &rt;
|
|
+ }
|
|
+
|
|
+ struct futex_wait_block waitblock[mutexcount];
|
|
+
|
|
+ for (int i = 0; i < mutexcount; i++)
|
|
+ {
|
|
+ waitblock[i].uaddr = &mutexlist[i].__data.__lock;
|
|
+ waitblock[i].val = 2;
|
|
+ waitblock[i].bitset = ~0;
|
|
+ }
|
|
+
|
|
+ long int __ret;
|
|
+
|
|
+ /* Safe to use the flag for the first one, since all their types match. */
|
|
+ int private_flag = PTHREAD_MUTEX_PSHARED (&mutexlist[0]);
|
|
+
|
|
+ __ret = lll_futex_timed_wait_multiple (waitblock, mutexcount, timeout,
|
|
+ private_flag);
|
|
+
|
|
+ if (__ret < 0)
|
|
+ return -__ret; /* TODO is this correct? */
|
|
+
|
|
+ /* Have slept, try grabbing the one that woke us up? */
|
|
+ if (atomic_exchange_acq (&mutexlist[__ret].__data.__lock, 2) == 0)
|
|
+ {
|
|
+ /* We got it, done, loop will end below. */
|
|
+ result = __ret;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (result != -1)
|
|
+ {
|
|
+ /* Record the ownership. */
|
|
+ mutexlist[result].__data.__owner = id;
|
|
+ ++mutexlist[result].__data.__nusers;
|
|
+
|
|
+ /* Let the user know which mutex is now locked. */
|
|
+ *outlocked = result;
|
|
+
|
|
+ result = 0;
|
|
+ }
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+weak_alias (__pthread_mutex_timedlock_any, pthread_mutex_timedlock_any)
|
|
diff --git a/sysdeps/nptl/lowlevellock-futex.h b/sysdeps/nptl/lowlevellock-futex.h
|
|
index 2209ca76a1..83f32bc8e8 100644
|
|
--- a/sysdeps/nptl/lowlevellock-futex.h
|
|
+++ b/sysdeps/nptl/lowlevellock-futex.h
|
|
@@ -38,6 +38,7 @@
|
|
#define FUTEX_WAKE_BITSET 10
|
|
#define FUTEX_WAIT_REQUEUE_PI 11
|
|
#define FUTEX_CMP_REQUEUE_PI 12
|
|
+#define FUTEX_WAIT_MULTIPLE 13
|
|
#define FUTEX_PRIVATE_FLAG 128
|
|
#define FUTEX_CLOCK_REALTIME 256
|
|
|
|
@@ -73,6 +74,14 @@
|
|
? -INTERNAL_SYSCALL_ERRNO (__ret) : 0); \
|
|
})
|
|
|
|
+# define lll_futex_syscall_ret(nargs, futexp, op, ...) \
|
|
+ ({ \
|
|
+ long int __ret = INTERNAL_SYSCALL (futex, nargs, futexp, op, \
|
|
+ __VA_ARGS__); \
|
|
+ (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (__ret)) \
|
|
+ ? -INTERNAL_SYSCALL_ERRNO (__ret) : __ret); \
|
|
+ })
|
|
+
|
|
/* For most of these macros, the return value is never really used.
|
|
Nevertheless, the protocol is that each one returns a negated errno
|
|
code for failure or zero for success. (Note that the corresponding
|
|
@@ -89,6 +98,11 @@
|
|
__lll_private_flag (FUTEX_WAIT, private), \
|
|
val, timeout)
|
|
|
|
+# define lll_futex_timed_wait_multiple(futexp, val, timeout, private) \
|
|
+ lll_futex_syscall_ret (4, futexp, \
|
|
+ __lll_private_flag (FUTEX_WAIT_MULTIPLE, private), \
|
|
+ val, timeout)
|
|
+
|
|
/* Verify whether the supplied clockid is supported by
|
|
lll_futex_clock_wait_bitset. */
|
|
# define lll_futex_supported_clockid(clockid) \
|
|
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
|
|
index 8a403cbf36..f1f7332472 100644
|
|
--- a/sysdeps/nptl/pthread.h
|
|
+++ b/sysdeps/nptl/pthread.h
|
|
@@ -753,7 +753,17 @@ extern int pthread_mutex_trylock (pthread_mutex_t *__mutex)
|
|
extern int pthread_mutex_lock (pthread_mutex_t *__mutex)
|
|
__THROWNL __nonnull ((1));
|
|
|
|
+/* Lock any one of several mutexes. */
|
|
+extern int pthread_mutex_lock_any (pthread_mutex_t *__mutexlist,
|
|
+ int mutexcount, int *outlocked);
|
|
+
|
|
#ifdef __USE_XOPEN2K
|
|
+/* Lock any one of several mutexes, with timeout. */
|
|
+extern int pthread_mutex_timedlock_any (pthread_mutex_t *__mutexlist,
|
|
+ int mutexcount,
|
|
+ const struct timespec *__restrict
|
|
+ __abstime, int *outlocked);
|
|
+
|
|
/* Wait until lock becomes available, or specified time passes. */
|
|
extern int pthread_mutex_timedlock (pthread_mutex_t *__restrict __mutex,
|
|
const struct timespec *__restrict
|
|
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
|
|
index 971269d2ef..54d966516c 100644
|
|
--- a/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
|
|
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
|
|
@@ -105,6 +105,7 @@ GLIBC_2.2.5 pthread_kill_other_threads_np F
|
|
GLIBC_2.2.5 pthread_mutex_destroy F
|
|
GLIBC_2.2.5 pthread_mutex_init F
|
|
GLIBC_2.2.5 pthread_mutex_lock F
|
|
+GLIBC_2.2.5 pthread_mutex_lock_any F
|
|
GLIBC_2.2.5 pthread_mutex_timedlock F
|
|
GLIBC_2.2.5 pthread_mutex_trylock F
|
|
GLIBC_2.2.5 pthread_mutex_unlock F
|