/* Make sure pthread_mutex_timedlock doesn't return spurious error codes. Copyright (C) 2020-2024 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #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