diff options
author | Lucas A. M. Magalhaes <lamm@linux.ibm.com> | 2020-12-01 18:05:07 -0300 |
---|---|---|
committer | Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com> | 2020-12-01 18:26:26 -0300 |
commit | 61855081017dff30c577855cda882740356b5d98 (patch) | |
tree | 69472c92f3f8b65c240b5987bbeee2760108933b /nptl | |
parent | 33fc34521de970153344cfe1bfa9ce6da7a6efea (diff) | |
download | glibc-61855081017dff30c577855cda882740356b5d98.zip glibc-61855081017dff30c577855cda882740356b5d98.tar.gz glibc-61855081017dff30c577855cda882740356b5d98.tar.bz2 |
nptl: Fix __futex_clocklock64 return error check [BZ #26964]
The earlier implementation of this, __lll_clocklock, calls lll_clockwait
that doesn't return the futex syscall error codes. It always tries again
if that fails.
However in the current implementation, when the futex returns EAGAIN,
__futex_clocklock64 will also return EGAIN, even if the futex is taken.
This patch fixes the EAGAIN issue and also adds a check for EINTR. As
futex syscall can return EINTR if the thread is interrupted by a signal.
In this case I'm assuming the function should continue trying to lock as
there is no mention to about it on POSIX. Also add a test for both
scenarios.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'nptl')
-rw-r--r-- | nptl/Makefile | 2 | ||||
-rw-r--r-- | nptl/tst-pthread-timedlock-lockloop.c | 138 |
2 files changed, 139 insertions, 1 deletions
diff --git a/nptl/Makefile b/nptl/Makefile index 94d805f..ddd83df 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -298,7 +298,7 @@ tests = tst-attr2 tst-attr3 tst-default-attr \ tst-thread-affinity-sched \ tst-pthread-defaultattr-free \ tst-pthread-attr-sigmask \ - + tst-pthread-timedlock-lockloop tests-container = tst-pthread-getattr diff --git a/nptl/tst-pthread-timedlock-lockloop.c b/nptl/tst-pthread-timedlock-lockloop.c new file mode 100644 index 0000000..bd5ed77 --- /dev/null +++ b/nptl/tst-pthread-timedlock-lockloop.c @@ -0,0 +1,138 @@ +/* Make sure pthread_mutex_timedlock doesn't return spurious error codes. + + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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 + <https://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <support/check.h> +#include <support/timespec.h> +#include <support/xsignal.h> +#include <support/xthread.h> +#include <support/xtime.h> +#include <time.h> +#include <unistd.h> + +#define NANO_PER_SEC 1000000000LL +#define TIMEOUT (NANO_PER_SEC / 1000LL) +#define NUM_THREADS 50 +#define RETEST_TIMES 100 + +static pthread_mutex_t mutex; +static int runs; +static clockid_t clockid; + +static void +signal_handler (int sig_num) +{ + TEST_COMPARE (sig_num, SIGUSR1); +} + +/* Call pthread_mutex_timedlock()/pthread_mutex_unlock() repetitively, hoping + that one of them returns EAGAIN or EINTR unexpectedly. */ +static void * +worker_timedlock (void *arg) +{ + for (unsigned int run = 0; run < runs; run++) + { + struct timespec abs_time = timespec_add (xclock_now (CLOCK_REALTIME), + make_timespec (0, 1000000)); + + int ret = pthread_mutex_timedlock (&mutex, &abs_time); + + if (ret == 0) + xpthread_mutex_unlock (&mutex); + + TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT); + } + return NULL; +} + +static void * +worker_clocklock (void *arg) +{ + for (unsigned int run = 0; run < runs; run++) + { + struct timespec time = + timespec_add (xclock_now (clockid), make_timespec (0, 1000000)); + + int ret = pthread_mutex_clocklock (&mutex, clockid, &time); + + if (ret == 0) + xpthread_mutex_unlock (&mutex); + + TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT); + } + return NULL; +} + +static int +run_test_set (void *(*worker) (void *)) +{ + pthread_t workers[NUM_THREADS]; + + /* Check if default pthread_mutex_{timed,clock}lock with valid arguments + returns either 0 or ETIMEDOUT. Since there is no easy way to force + the error condition, the test creates multiple threads which in turn + issues pthread_mutex_timedlock multiple times. */ + runs = 100; + for (int run = 0; run < RETEST_TIMES; run++) + { + for (int i = 0; i < NUM_THREADS; i++) + workers[i] = xpthread_create (NULL, worker, NULL); + for (int i = 0; i < NUM_THREADS; i++) + xpthread_join (workers[i]); + } + + /* The idea is similar to previous tests, but we check if + pthread_mutex_{timed,clock}lock does not return EINTR. */ + pthread_t thread; + runs = 1; + for (int i = 0; i < RETEST_TIMES * 1000; i++) + { + xpthread_mutex_lock (&mutex); + thread = xpthread_create (NULL, worker, NULL); + /* Sleep just a little bit to reach the lock on the worker thread. */ + usleep (10); + pthread_kill (thread, SIGUSR1); + xpthread_mutex_unlock (&mutex); + xpthread_join (thread); + } + + return 0; +} + +static int +do_test (void) +{ + + xsignal (SIGUSR1, signal_handler); + + xpthread_mutex_init (&mutex, NULL); + + run_test_set (worker_timedlock); + clockid = CLOCK_REALTIME; + run_test_set (worker_clocklock); + clockid = CLOCK_MONOTONIC; + run_test_set (worker_clocklock); + return 0; +} + +#include <support/test-driver.c> |