1 | /* Hurd helpers for lowlevellocks. |
2 | Copyright (C) 1999-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include "hurdlock.h" |
20 | #include <hurd.h> |
21 | #include <hurd/hurd.h> |
22 | #include <time.h> |
23 | #include <errno.h> |
24 | #include <unistd.h> |
25 | |
26 | /* Convert an absolute timeout in nanoseconds to a relative |
27 | timeout in milliseconds. */ |
28 | static inline int __attribute__ ((gnu_inline)) |
29 | compute_reltime (const struct timespec *abstime, clockid_t clk) |
30 | { |
31 | struct timespec ts; |
32 | __clock_gettime (clk, &ts); |
33 | |
34 | ts.tv_sec = abstime->tv_sec - ts.tv_sec; |
35 | ts.tv_nsec = abstime->tv_nsec - ts.tv_nsec; |
36 | |
37 | if (ts.tv_nsec < 0) |
38 | { |
39 | --ts.tv_sec; |
40 | ts.tv_nsec += 1000000000; |
41 | } |
42 | |
43 | return ts.tv_sec < 0 ? -1 : (int)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000); |
44 | } |
45 | |
46 | int |
47 | __lll_abstimed_wait (void *ptr, int val, |
48 | const struct timespec *tsp, int flags, int clk) |
49 | { |
50 | if (clk != CLOCK_REALTIME) |
51 | return EINVAL; |
52 | |
53 | int mlsec = compute_reltime (abstime: tsp, clk); |
54 | return mlsec < 0 ? KERN_TIMEDOUT : __lll_timed_wait (ptr, val, mlsec, flags); |
55 | } |
56 | |
57 | int |
58 | __lll_abstimed_wait_intr (void *ptr, int val, |
59 | const struct timespec *tsp, int flags, int clk) |
60 | { |
61 | if (clk != CLOCK_REALTIME) |
62 | return EINVAL; |
63 | |
64 | int mlsec = compute_reltime (abstime: tsp, clk); |
65 | return mlsec < 0 ? KERN_TIMEDOUT : __lll_timed_wait_intr (ptr, val, mlsec, flags); |
66 | } |
67 | |
68 | int |
69 | __lll_abstimed_xwait (void *ptr, int lo, int hi, |
70 | const struct timespec *tsp, int flags, int clk) |
71 | { |
72 | if (clk != CLOCK_REALTIME) |
73 | return EINVAL; |
74 | |
75 | int mlsec = compute_reltime (abstime: tsp, clk); |
76 | return mlsec < 0 ? KERN_TIMEDOUT : __lll_timed_xwait (ptr, lo, hi, mlsec, |
77 | flags); |
78 | } |
79 | |
80 | int |
81 | __lll_abstimed_lock (void *ptr, |
82 | const struct timespec *tsp, int flags, int clk) |
83 | { |
84 | if (clk != CLOCK_REALTIME) |
85 | return EINVAL; |
86 | |
87 | if (__lll_trylock (ptr) == 0) |
88 | return 0; |
89 | |
90 | while (1) |
91 | { |
92 | if (atomic_exchange_acq ((int *)ptr, 2) == 0) |
93 | return 0; |
94 | else if (! valid_nanoseconds (ns: tsp->tv_nsec)) |
95 | return EINVAL; |
96 | |
97 | int mlsec = compute_reltime (abstime: tsp, clk); |
98 | if (mlsec < 0 || __lll_timed_wait (ptr, 2, mlsec, flags) == KERN_TIMEDOUT) |
99 | return ETIMEDOUT; |
100 | } |
101 | } |
102 | |
103 | /* Robust locks. */ |
104 | |
105 | /* Test if a given process id is still valid. */ |
106 | static inline int |
107 | valid_pid (int pid) |
108 | { |
109 | task_t task = __pid2task (pid); |
110 | if (task == MACH_PORT_NULL) |
111 | return 0; |
112 | |
113 | __mach_port_deallocate (__mach_task_self (), task); |
114 | return 1; |
115 | } |
116 | |
117 | /* Robust locks have currently no support from the kernel; they |
118 | are simply implemented with periodic polling. When sleeping, the |
119 | maximum blocking time is determined by this constant. */ |
120 | #define MAX_WAIT_TIME 1500 |
121 | |
122 | int |
123 | __lll_robust_lock (void *ptr, int flags) |
124 | { |
125 | int *iptr = (int *)ptr; |
126 | int id = __getpid (); |
127 | int wait_time = 25; |
128 | unsigned int val; |
129 | |
130 | /* Try to set the lock word to our PID if it's clear. Otherwise, |
131 | mark it as having waiters. */ |
132 | while (1) |
133 | { |
134 | val = *iptr; |
135 | if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0) |
136 | return 0; |
137 | else if (atomic_compare_and_exchange_bool_acq (iptr, |
138 | val | LLL_WAITERS, val) == 0) |
139 | break; |
140 | } |
141 | |
142 | for (id |= LLL_WAITERS ; ; ) |
143 | { |
144 | val = *iptr; |
145 | if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0) |
146 | return 0; |
147 | else if (val && !valid_pid (pid: val & LLL_OWNER_MASK)) |
148 | { |
149 | if (atomic_compare_and_exchange_bool_acq (iptr, id, val) == 0) |
150 | return EOWNERDEAD; |
151 | } |
152 | else |
153 | { |
154 | __lll_timed_wait (iptr, val, wait_time, flags); |
155 | if (wait_time < MAX_WAIT_TIME) |
156 | wait_time <<= 1; |
157 | } |
158 | } |
159 | } |
160 | |
161 | int |
162 | __lll_robust_abstimed_lock (void *ptr, |
163 | const struct timespec *tsp, int flags, int clk) |
164 | { |
165 | int *iptr = (int *)ptr; |
166 | int id = __getpid (); |
167 | int wait_time = 25; |
168 | unsigned int val; |
169 | |
170 | if (clk != CLOCK_REALTIME) |
171 | return EINVAL; |
172 | |
173 | while (1) |
174 | { |
175 | val = *iptr; |
176 | if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0) |
177 | return 0; |
178 | else if (atomic_compare_and_exchange_bool_acq (iptr, |
179 | val | LLL_WAITERS, val) == 0) |
180 | break; |
181 | } |
182 | |
183 | for (id |= LLL_WAITERS ; ; ) |
184 | { |
185 | val = *iptr; |
186 | if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0) |
187 | return 0; |
188 | else if (val && !valid_pid (pid: val & LLL_OWNER_MASK)) |
189 | { |
190 | if (atomic_compare_and_exchange_bool_acq (iptr, id, val) == 0) |
191 | return EOWNERDEAD; |
192 | } |
193 | else |
194 | { |
195 | int mlsec = compute_reltime (abstime: tsp, clk); |
196 | if (mlsec < 0) |
197 | return ETIMEDOUT; |
198 | else if (mlsec > wait_time) |
199 | mlsec = wait_time; |
200 | |
201 | int res = __lll_timed_wait (iptr, val, mlsec, flags); |
202 | if (res == KERN_TIMEDOUT) |
203 | return ETIMEDOUT; |
204 | else if (wait_time < MAX_WAIT_TIME) |
205 | wait_time <<= 1; |
206 | } |
207 | } |
208 | } |
209 | |
210 | int |
211 | __lll_robust_trylock (void *ptr) |
212 | { |
213 | int *iptr = (int *)ptr; |
214 | int id = __getpid (); |
215 | unsigned int val = *iptr; |
216 | |
217 | if (!val) |
218 | { |
219 | if (atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0) |
220 | return 0; |
221 | } |
222 | else if (!valid_pid (pid: val & LLL_OWNER_MASK) |
223 | && atomic_compare_and_exchange_bool_acq (iptr, id, val) == 0) |
224 | return EOWNERDEAD; |
225 | |
226 | return EBUSY; |
227 | } |
228 | |
229 | void |
230 | __lll_robust_unlock (void *ptr, int flags) |
231 | { |
232 | unsigned int val = atomic_load_relaxed ((unsigned int *)ptr); |
233 | while (1) |
234 | { |
235 | if (val & LLL_WAITERS) |
236 | { |
237 | __lll_set_wake (ptr, 0, flags); |
238 | break; |
239 | } |
240 | else if (atomic_compare_exchange_weak_release ((unsigned int *)ptr, &val, 0)) |
241 | break; |
242 | } |
243 | } |
244 | |