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 |
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 * |
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 (s: 0, ns: 1000000)); |
57 | |
58 | int ret = pthread_mutex_timedlock (mutex: &mutex, abstime: &abs_time); |
59 | |
60 | if (ret == 0) |
61 | xpthread_mutex_unlock (mutex: &mutex); |
62 | |
63 | TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT); |
64 | } |
65 | return NULL; |
66 | } |
67 | |
68 | static void * |
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 (clock: clockid), make_timespec (s: 0, ns: 1000000)); |
75 | |
76 | int ret = pthread_mutex_clocklock (mutex: &mutex, clockid: clockid, abstime: &time); |
77 | |
78 | if (ret == 0) |
79 | xpthread_mutex_unlock (mutex: &mutex); |
80 | |
81 | TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT); |
82 | } |
83 | return NULL; |
84 | } |
85 | |
86 | static int |
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, thread_func: worker, NULL); |
100 | for (int i = 0; i < NUM_THREADS; i++) |
101 | xpthread_join (thr: 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: &mutex); |
111 | thread = xpthread_create (NULL, thread_func: worker, NULL); |
112 | /* Sleep just a little bit to reach the lock on the worker thread. */ |
113 | usleep (useconds: 10); |
114 | pthread_kill (threadid: thread, SIGUSR1); |
115 | xpthread_mutex_unlock (mutex: &mutex); |
116 | xpthread_join (thr: thread); |
117 | } |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static int |
123 | do_test (void) |
124 | { |
125 | |
126 | xsignal (SIGUSR1, handler: signal_handler); |
127 | |
128 | xpthread_mutex_init (&mutex, NULL); |
129 | |
130 | run_test_set (worker: worker_timedlock); |
131 | clockid = CLOCK_REALTIME; |
132 | run_test_set (worker: worker_clocklock); |
133 | clockid = CLOCK_MONOTONIC; |
134 | run_test_set (worker: worker_clocklock); |
135 | return 0; |
136 | } |
137 | |
138 | #include <support/test-driver.c> |
139 | |