Skip to content
Permalink
Browse files

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.
  • Loading branch information...
Plagman committed Jul 11, 2019
1 parent c86b8e7 commit 3b01145fa25987f2f93e7eda7f3e7d0f2f77b290
@@ -68,6 +68,7 @@ libpthread-routines = nptl-init nptlfreeres vars events version pt-interp \
pthread_getattr_np \
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 \
@@ -274,6 +274,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 {
@@ -390,6 +390,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)
@@ -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)
@@ -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)
@@ -763,7 +763,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
@@ -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

@@ -74,6 +75,15 @@
? -INTERNAL_SYSCALL_ERRNO (__ret, __err) : 0); \
})

#define lll_futex_syscall_ret(nargs, futexp, op, ...) \
({ \
INTERNAL_SYSCALL_DECL (__err); \
long int __ret = INTERNAL_SYSCALL (futex, __err, nargs, futexp, op, \
__VA_ARGS__); \
(__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (__ret, __err)) \
? -INTERNAL_SYSCALL_ERRNO (__ret, __err) : __ret); \
})

#define lll_futex_wait(futexp, val, private) \
lll_futex_timed_wait (futexp, val, NULL, private)

@@ -82,6 +92,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) \
@@ -126,6 +126,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

0 comments on commit 3b01145

Please sign in to comment.
You can’t perform that action at this time.