1 /* Make sure pthread_mutex_timedlock doesn't return spurious error codes.
2 
3    Copyright (C) 2020-2022 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
19 
20 #include <errno.h>
21 #include <pthread.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <support/check.h>
26 #include <support/timespec.h>
27 #include <support/xsignal.h>
28 #include <support/xthread.h>
29 #include <support/xtime.h>
30 #include <time.h>
31 #include <unistd.h>
32 
33 #define NANO_PER_SEC 1000000000LL
34 #define TIMEOUT (NANO_PER_SEC / 1000LL)
35 #define NUM_THREADS 50
36 #define RETEST_TIMES 100
37 
38 static pthread_mutex_t mutex;
39 static int runs;
40 static clockid_t clockid;
41 
42 static void
signal_handler(int sig_num)43 signal_handler (int sig_num)
44 {
45   TEST_COMPARE (sig_num, SIGUSR1);
46 }
47 
48 /* Call pthread_mutex_timedlock()/pthread_mutex_unlock() repetitively, hoping
49    that one of them returns EAGAIN or EINTR unexpectedly.  */
50 static void *
worker_timedlock(void * arg)51 worker_timedlock (void *arg)
52 {
53   for (unsigned int run = 0; run < runs; run++)
54     {
55       struct timespec abs_time = timespec_add (xclock_now (CLOCK_REALTIME),
56 					       make_timespec (0, 1000000));
57 
58       int ret = pthread_mutex_timedlock (&mutex, &abs_time);
59 
60       if (ret == 0)
61 	xpthread_mutex_unlock (&mutex);
62 
63       TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT);
64     }
65   return NULL;
66 }
67 
68 static void *
worker_clocklock(void * arg)69 worker_clocklock (void *arg)
70 {
71   for (unsigned int run = 0; run < runs; run++)
72     {
73       struct timespec time =
74 	timespec_add (xclock_now (clockid), make_timespec (0, 1000000));
75 
76       int ret = pthread_mutex_clocklock (&mutex, clockid, &time);
77 
78       if (ret == 0)
79 	xpthread_mutex_unlock (&mutex);
80 
81       TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT);
82     }
83   return NULL;
84 }
85 
86 static int
run_test_set(void * (* worker)(void *))87 run_test_set (void *(*worker) (void *))
88 {
89   pthread_t workers[NUM_THREADS];
90 
91   /* Check if default pthread_mutex_{timed,clock}lock with valid arguments
92      returns either 0 or ETIMEDOUT.  Since there is no easy way to force
93      the error condition, the test creates multiple threads which in turn
94      issues pthread_mutex_timedlock multiple times.  */
95   runs = 100;
96   for (int run = 0; run < RETEST_TIMES; run++)
97     {
98       for (int i = 0; i < NUM_THREADS; i++)
99 	workers[i] = xpthread_create (NULL, worker, NULL);
100       for (int i = 0; i < NUM_THREADS; i++)
101 	xpthread_join (workers[i]);
102     }
103 
104   /* The idea is similar to previous tests, but we check if
105      pthread_mutex_{timed,clock}lock does not return EINTR.  */
106   pthread_t thread;
107   runs = 1;
108   for (int i = 0; i < RETEST_TIMES * 1000; i++)
109     {
110       xpthread_mutex_lock (&mutex);
111       thread = xpthread_create (NULL, worker, NULL);
112       /* Sleep just a little bit to reach the lock on the worker thread.  */
113       usleep (10);
114       pthread_kill (thread, SIGUSR1);
115       xpthread_mutex_unlock (&mutex);
116       xpthread_join (thread);
117     }
118 
119   return 0;
120 }
121 
122 static int
do_test(void)123 do_test (void)
124 {
125 
126   xsignal (SIGUSR1, signal_handler);
127 
128   xpthread_mutex_init (&mutex, NULL);
129 
130   run_test_set (worker_timedlock);
131   clockid = CLOCK_REALTIME;
132   run_test_set (worker_clocklock);
133   clockid = CLOCK_MONOTONIC;
134   run_test_set (worker_clocklock);
135   return 0;
136 }
137 
138 #include <support/test-driver.c>
139