1 | /* Copyright (C) 2003-2024 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <errno.h> |
19 | #include <pthread.h> |
20 | #include <stdint.h> |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <unistd.h> |
25 | #include <sys/mman.h> |
26 | #include <sys/time.h> |
27 | #include <sys/wait.h> |
28 | #include <support/check.h> |
29 | #include <support/timespec.h> |
30 | #include <support/xunistd.h> |
31 | #include <support/xthread.h> |
32 | |
33 | #ifdef ENABLE_PP |
34 | #include "tst-tpp.h" |
35 | #endif |
36 | |
37 | |
38 | /* A bogus clock value that tells run_test to use pthread_mutex_timedlock |
39 | rather than pthread_mutex_clocklock. */ |
40 | #define CLOCK_USE_TIMEDLOCK (-1) |
41 | |
42 | static void |
43 | do_test_clock (clockid_t clockid, int tmo_result) |
44 | { |
45 | const clockid_t clockid_for_get = |
46 | (clockid == CLOCK_USE_TIMEDLOCK) ? CLOCK_REALTIME : clockid; |
47 | size_t ps = sysconf (_SC_PAGESIZE); |
48 | char tmpfname[] = "/tmp/tst-mutex9.XXXXXX" ; |
49 | char data[ps]; |
50 | void *mem; |
51 | int fd; |
52 | pthread_mutex_t *m; |
53 | pthread_mutexattr_t a; |
54 | pid_t pid; |
55 | |
56 | fd = mkstemp (template: tmpfname); |
57 | if (fd == -1) |
58 | FAIL_EXIT1 ("cannot open temporary file: %m\n" ); |
59 | |
60 | /* Make sure it is always removed. */ |
61 | unlink (name: tmpfname); |
62 | |
63 | /* Create one page of data. */ |
64 | memset (data, '\0', ps); |
65 | |
66 | /* Write the data to the file. */ |
67 | xwrite (fd, data, ps); |
68 | |
69 | mem = xmmap (NULL, length: ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd); |
70 | |
71 | m = (pthread_mutex_t *) (((uintptr_t) mem + __alignof (pthread_mutex_t)) |
72 | & ~(__alignof (pthread_mutex_t) - 1)); |
73 | |
74 | TEST_COMPARE (pthread_mutexattr_init (&a), 0); |
75 | |
76 | TEST_COMPARE (pthread_mutexattr_setpshared (&a, PTHREAD_PROCESS_SHARED), 0); |
77 | |
78 | TEST_COMPARE (pthread_mutexattr_settype (&a, PTHREAD_MUTEX_RECURSIVE), 0); |
79 | |
80 | #if defined ENABLE_PI |
81 | TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT), 0); |
82 | #elif defined ENABLE_PP |
83 | TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_PROTECT), 0); |
84 | TEST_COMPARE (pthread_mutexattr_setprioceiling (&a, 6), 0); |
85 | #endif |
86 | |
87 | int e; |
88 | if ((e = pthread_mutex_init (mutex: m, mutexattr: &a)) != 0) |
89 | { |
90 | #ifdef ENABLE_PI |
91 | if (e == ENOTSUP) |
92 | FAIL_UNSUPPORTED ("PI mutexes unsupported" ); |
93 | #endif |
94 | FAIL_EXIT1 ("mutex_init failed" ); |
95 | } |
96 | |
97 | TEST_COMPARE (pthread_mutex_lock (m), 0); |
98 | |
99 | TEST_COMPARE (pthread_mutexattr_destroy (&a), 0); |
100 | |
101 | puts (s: "going to fork now" ); |
102 | pid = xfork (); |
103 | if (pid == 0) |
104 | { |
105 | if (pthread_mutex_trylock (mutex: m) == 0) |
106 | FAIL_EXIT1 ("child: mutex_trylock succeeded" ); |
107 | |
108 | if (pthread_mutex_unlock (mutex: m) == 0) |
109 | FAIL_EXIT1 ("child: mutex_unlock succeeded" ); |
110 | |
111 | const struct timespec ts = timespec_add (xclock_now (clock: clockid_for_get), |
112 | make_timespec (s: 0, ns: 500000000)); |
113 | |
114 | if (clockid == CLOCK_USE_TIMEDLOCK) |
115 | TEST_COMPARE (pthread_mutex_timedlock (m, &ts), tmo_result); |
116 | else |
117 | TEST_COMPARE (pthread_mutex_clocklock (m, clockid, &ts), tmo_result); |
118 | |
119 | alarm (1); |
120 | |
121 | pthread_mutex_lock (mutex: m); |
122 | |
123 | puts (s: "child: mutex_lock returned" ); |
124 | |
125 | exit (0); |
126 | } |
127 | |
128 | sleep (seconds: 2); |
129 | |
130 | int status; |
131 | if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) |
132 | FAIL_EXIT1 ("waitpid failed" ); |
133 | if (! WIFSIGNALED (status)) |
134 | FAIL_EXIT1 ("child not killed by signal" ); |
135 | TEST_COMPARE (WTERMSIG (status), SIGALRM); |
136 | } |
137 | |
138 | static int |
139 | do_test (void) |
140 | { |
141 | #ifdef ENABLE_PP |
142 | init_tpp_test (); |
143 | #endif |
144 | |
145 | int monotonic_result = |
146 | #ifdef ENABLE_PI |
147 | support_mutex_pi_monotonic () ? ETIMEDOUT : EINVAL; |
148 | #else |
149 | ETIMEDOUT; |
150 | #endif |
151 | |
152 | do_test_clock (CLOCK_USE_TIMEDLOCK, ETIMEDOUT); |
153 | do_test_clock (CLOCK_REALTIME, ETIMEDOUT); |
154 | do_test_clock (CLOCK_MONOTONIC, tmo_result: monotonic_result); |
155 | return 0; |
156 | } |
157 | |
158 | #include <support/test-driver.c> |
159 | |