| | varnish-cache/bin/varnishd/cache/cache_lck.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2008-2011 Varnish Software AS |
2 |
|
* All rights reserved. |
3 |
|
* |
4 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
5 |
|
* |
6 |
|
* SPDX-License-Identifier: BSD-2-Clause |
7 |
|
* |
8 |
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
* modification, are permitted provided that the following conditions |
10 |
|
* are met: |
11 |
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
* documentation and/or other materials provided with the distribution. |
16 |
|
* |
17 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
|
* SUCH DAMAGE. |
28 |
|
* |
29 |
|
* The geniuses who came up with pthreads did not think operations like |
30 |
|
* pthread_assert_mutex_held() were important enough to include them in |
31 |
|
* the API. |
32 |
|
* |
33 |
|
* Build our own locks on top of pthread mutexes and hope that the next |
34 |
|
* civilization is better at such crucial details than this one. |
35 |
|
*/ |
36 |
|
|
37 |
|
#include "config.h" |
38 |
|
|
39 |
|
#include "cache_varnishd.h" |
40 |
|
|
41 |
|
#include <stdlib.h> |
42 |
|
#include <stdio.h> |
43 |
|
|
44 |
|
#include "vtim.h" |
45 |
|
|
46 |
|
#include "VSC_lck.h" |
47 |
|
|
48 |
|
struct ilck { |
49 |
|
unsigned magic; |
50 |
|
#define ILCK_MAGIC 0x7b86c8a5 |
51 |
|
int held; |
52 |
|
pthread_mutex_t mtx; |
53 |
|
pthread_t owner; |
54 |
|
const char *w; |
55 |
|
struct VSC_lck *stat; |
56 |
|
}; |
57 |
|
|
58 |
|
/*--------------------------------------------------------------------*/ |
59 |
|
|
60 |
|
static void |
61 |
8167 |
Lck_Witness_Lock(const struct ilck *il, const char *p, int l, |
62 |
|
const char *attempt) |
63 |
|
{ |
64 |
|
char *q, t[10]; //lint -e429 |
65 |
|
int emit; |
66 |
|
|
67 |
8167 |
AN(p); |
68 |
8167 |
q = pthread_getspecific(witness_key); |
69 |
8167 |
if (q == NULL) { |
70 |
997 |
q = calloc(1, 1024); |
71 |
997 |
AN(q); |
72 |
997 |
PTOK(pthread_setspecific(witness_key, q)); |
73 |
997 |
} |
74 |
8167 |
emit = *q != '\0'; |
75 |
8167 |
strcat(q, " "); |
76 |
8167 |
strcat(q, il->w); |
77 |
8167 |
strcat(q, attempt); |
78 |
8167 |
strcat(q, ","); |
79 |
8167 |
strcat(q, p); |
80 |
8167 |
strcat(q, ","); |
81 |
8167 |
bprintf(t, "%d", l); |
82 |
8167 |
strcat(q, t); |
83 |
8167 |
if (emit) |
84 |
720 |
VSL(SLT_Witness, NO_VXID, "%s", q); |
85 |
8167 |
} |
86 |
|
|
87 |
|
static void |
88 |
8311 |
Lck_Witness_Unlock(const struct ilck *il) |
89 |
|
{ |
90 |
|
char *q, *r; |
91 |
|
|
92 |
8311 |
q = pthread_getspecific(witness_key); |
93 |
8311 |
if (q == NULL) |
94 |
513 |
return; |
95 |
7798 |
r = strrchr(q, ' '); |
96 |
7798 |
if (r == NULL) |
97 |
0 |
r = q; |
98 |
|
else |
99 |
7798 |
*r++ = '\0'; |
100 |
7798 |
if (memcmp(r, il->w, strlen(il->w))) |
101 |
0 |
VSL(SLT_Witness, NO_VXID, "Unlock %s @ %s <%s>", il->w, r, q); |
102 |
|
else |
103 |
7798 |
*r = '\0'; |
104 |
8311 |
} |
105 |
|
|
106 |
|
/*--------------------------------------------------------------------*/ |
107 |
|
|
108 |
|
void v_matchproto_() |
109 |
12262893 |
Lck__Lock(struct lock *lck, const char *p, int l) |
110 |
|
{ |
111 |
|
struct ilck *ilck; |
112 |
12262893 |
int r = EINVAL; |
113 |
|
|
114 |
12262893 |
AN(lck); |
115 |
12262893 |
CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC); |
116 |
12262893 |
if (DO_DEBUG(DBG_WITNESS)) |
117 |
7959 |
Lck_Witness_Lock(ilck, p, l, ""); |
118 |
12262893 |
if (DO_DEBUG(DBG_LCK)) { |
119 |
0 |
r = pthread_mutex_trylock(&ilck->mtx); |
120 |
0 |
assert(r == 0 || r == EBUSY); |
121 |
0 |
} |
122 |
12262893 |
if (r) |
123 |
12262675 |
PTOK(pthread_mutex_lock(&ilck->mtx)); |
124 |
12262893 |
AZ(ilck->held); |
125 |
12262871 |
if (r == EBUSY) |
126 |
0 |
ilck->stat->dbg_busy++; |
127 |
12262871 |
ilck->stat->locks++; |
128 |
12262871 |
ilck->owner = pthread_self(); |
129 |
12262871 |
ilck->held = 1; |
130 |
12262871 |
} |
131 |
|
|
132 |
|
void v_matchproto_() |
133 |
13428272 |
Lck__Unlock(struct lock *lck, const char *p, int l) |
134 |
|
{ |
135 |
|
struct ilck *ilck; |
136 |
|
|
137 |
13428272 |
(void)p; |
138 |
13428272 |
(void)l; |
139 |
|
|
140 |
13428272 |
AN(lck); |
141 |
13428272 |
CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC); |
142 |
13428272 |
assert(pthread_equal(ilck->owner, pthread_self())); |
143 |
13428272 |
AN(ilck->held); |
144 |
13428272 |
ilck->held = 0; |
145 |
|
/* |
146 |
|
* #ifdef POSIX_STUPIDITY: |
147 |
|
* The pthread_t type has no defined assignment or comparison |
148 |
|
* operators, this is why pthread_equal() is necessary. |
149 |
|
* Unfortunately POSIX forgot to define a NULL value for pthread_t |
150 |
|
* so you can never unset a pthread_t variable. |
151 |
|
* We hack it and fill it with zero bits, hoping for sane |
152 |
|
* implementations of pthread. |
153 |
|
* #endif |
154 |
|
*/ |
155 |
|
#ifdef PTHREAD_NULL |
156 |
|
ilck->owner = PTHREAD_NULL; |
157 |
|
#else |
158 |
13428272 |
memset(&ilck->owner, 0, sizeof ilck->owner); |
159 |
|
#endif |
160 |
13428272 |
PTOK(pthread_mutex_unlock(&ilck->mtx)); |
161 |
13428272 |
if (DO_DEBUG(DBG_WITNESS)) |
162 |
8310 |
Lck_Witness_Unlock(ilck); |
163 |
13428272 |
} |
164 |
|
|
165 |
|
int v_matchproto_() |
166 |
1942205 |
Lck__Trylock(struct lock *lck, const char *p, int l) |
167 |
|
{ |
168 |
|
struct ilck *ilck; |
169 |
|
int r; |
170 |
|
|
171 |
1942205 |
AN(lck); |
172 |
1942205 |
CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC); |
173 |
1942205 |
if (DO_DEBUG(DBG_WITNESS)) |
174 |
206 |
Lck_Witness_Lock(ilck, p, l, "?"); |
175 |
1942205 |
r = pthread_mutex_trylock(&ilck->mtx); |
176 |
1942205 |
assert(r == 0 || r == EBUSY); |
177 |
1942205 |
if (r == 0) { |
178 |
1942195 |
AZ(ilck->held); |
179 |
1942195 |
ilck->held = 1; |
180 |
1942195 |
ilck->stat->locks++; |
181 |
1942195 |
ilck->owner = pthread_self(); |
182 |
1942205 |
} else if (DO_DEBUG(DBG_LCK)) |
183 |
0 |
ilck->stat->dbg_try_fail++; |
184 |
1942205 |
return (r); |
185 |
|
} |
186 |
|
|
187 |
|
int |
188 |
2510129 |
Lck__Held(const struct lock *lck) |
189 |
|
{ |
190 |
|
struct ilck *ilck; |
191 |
|
|
192 |
2510129 |
AN(lck); |
193 |
2510129 |
CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC); |
194 |
2510129 |
return (ilck->held); |
195 |
|
} |
196 |
|
|
197 |
|
int |
198 |
2510179 |
Lck__Owned(const struct lock *lck) |
199 |
|
{ |
200 |
|
struct ilck *ilck; |
201 |
|
|
202 |
2510179 |
AN(lck); |
203 |
2510179 |
CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC); |
204 |
2510179 |
AN(ilck->held); |
205 |
2510179 |
return (pthread_equal(ilck->owner, pthread_self())); |
206 |
|
} |
207 |
|
|
208 |
|
int v_matchproto_() |
209 |
1268665 |
Lck_CondWait(pthread_cond_t *cond, struct lock *lck) |
210 |
|
{ |
211 |
1268665 |
return (Lck_CondWaitUntil(cond, lck, INFINITY)); |
212 |
|
} |
213 |
|
|
214 |
|
int v_matchproto_() |
215 |
298801 |
Lck_CondWaitTimeout(pthread_cond_t *cond, struct lock *lck, vtim_dur timeout) |
216 |
|
{ |
217 |
|
|
218 |
298801 |
if (isinf(timeout)) |
219 |
0 |
return (Lck_CondWaitUntil(cond, lck, INFINITY)); |
220 |
|
|
221 |
298801 |
assert(timeout >= 0); |
222 |
298801 |
assert(timeout <= 3600); |
223 |
298801 |
timeout = vmax(timeout, 1e-3); |
224 |
298801 |
return (Lck_CondWaitUntil(cond, lck, VTIM_real() + timeout)); |
225 |
298801 |
} |
226 |
|
|
227 |
|
int v_matchproto_() |
228 |
1918426 |
Lck_CondWaitUntil(pthread_cond_t *cond, struct lock *lck, vtim_real when) |
229 |
|
{ |
230 |
|
struct ilck *ilck; |
231 |
|
struct timespec ts; |
232 |
|
|
233 |
1918426 |
AN(lck); |
234 |
1918426 |
CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC); |
235 |
1918426 |
AN(ilck->held); |
236 |
1918426 |
assert(pthread_equal(ilck->owner, pthread_self())); |
237 |
1918426 |
ilck->held = 0; |
238 |
1918426 |
if (isinf(when)) { |
239 |
1500309 |
errno = pthread_cond_wait(cond, &ilck->mtx); |
240 |
1500309 |
AZ(errno); |
241 |
1500309 |
} else { |
242 |
418117 |
assert(when > 1e9); |
243 |
418117 |
ts = VTIM_timespec(when); |
244 |
418117 |
assert(ts.tv_nsec >= 0 && ts.tv_nsec <= 999999999); |
245 |
418117 |
errno = pthread_cond_timedwait(cond, &ilck->mtx, &ts); |
246 |
|
#if defined (__APPLE__) |
247 |
|
/* |
248 |
|
* I hate woo-doo programming in all it's forms and all it's |
249 |
|
* manifestations, but for reasons I utterly fail to isolate, |
250 |
|
* OSX sometimes throws an EINVAL. |
251 |
|
* |
252 |
|
* I have tried very hard to determine if any of the three |
253 |
|
* arguments are in fact invalid, and found nothing which |
254 |
|
* even hints that it might be the case. |
255 |
|
* |
256 |
|
* So far I have yet to see a failure if the exact same |
257 |
|
* call is repeated after a very short sleep. |
258 |
|
* |
259 |
|
* Calling pthread_yield_np() instead of sleeping /mostly/ |
260 |
|
* works as well, but still fails sometimes. |
261 |
|
* |
262 |
|
* Env: |
263 |
|
* Darwin Kernel Version 20.5.0: |
264 |
|
* Sat May 8 05:10:31 PDT 2021; |
265 |
|
* root:xnu-7195.121.3~9/RELEASE_ARM64_T8101 arm64 |
266 |
|
* |
267 |
|
* 20220329 /phk |
268 |
|
*/ |
269 |
|
if (errno == EINVAL) { |
270 |
|
usleep(100); |
271 |
|
errno = pthread_cond_timedwait(cond, &ilck->mtx, &ts); |
272 |
|
} |
273 |
|
#endif |
274 |
|
/* We should never observe EINTR, but we have in the past. For |
275 |
|
* example when sanitizers are enabled. |
276 |
|
*/ |
277 |
418117 |
assert(errno == 0 || |
278 |
|
errno == ETIMEDOUT || |
279 |
|
errno == EINTR); |
280 |
|
} |
281 |
1918426 |
AZ(ilck->held); |
282 |
1918426 |
ilck->held = 1; |
283 |
1918426 |
ilck->owner = pthread_self(); |
284 |
1918426 |
return (errno); |
285 |
|
} |
286 |
|
|
287 |
|
void |
288 |
1365987 |
Lck__New(struct lock *lck, struct VSC_lck *st, const char *w) |
289 |
|
{ |
290 |
|
struct ilck *ilck; |
291 |
|
|
292 |
1365987 |
AN(st); |
293 |
1365987 |
AN(w); |
294 |
1365987 |
AN(lck); |
295 |
1365987 |
AZ(lck->priv); |
296 |
1365987 |
ALLOC_OBJ(ilck, ILCK_MAGIC); |
297 |
1365987 |
AN(ilck); |
298 |
1365987 |
ilck->w = w; |
299 |
1365987 |
ilck->stat = st; |
300 |
1365987 |
ilck->stat->creat++; |
301 |
1365987 |
PTOK(pthread_mutex_init(&ilck->mtx, &mtxattr_errorcheck)); |
302 |
1365987 |
lck->priv = ilck; |
303 |
1365987 |
} |
304 |
|
|
305 |
|
void |
306 |
215674 |
Lck_Delete(struct lock *lck) |
307 |
|
{ |
308 |
|
struct ilck *ilck; |
309 |
|
|
310 |
215674 |
AN(lck); |
311 |
215674 |
TAKE_OBJ_NOTNULL(ilck, &lck->priv, ILCK_MAGIC); |
312 |
215674 |
ilck->stat->destroy++; |
313 |
215674 |
PTOK(pthread_mutex_destroy(&ilck->mtx)); |
314 |
215674 |
FREE_OBJ(ilck); |
315 |
215674 |
} |
316 |
|
|
317 |
|
struct VSC_lck * |
318 |
808187 |
Lck_CreateClass(struct vsc_seg **sg, const char *name) |
319 |
|
{ |
320 |
808187 |
return (VSC_lck_New(NULL, sg, name)); |
321 |
|
} |
322 |
|
|
323 |
|
void |
324 |
0 |
Lck_DestroyClass(struct vsc_seg **sg) |
325 |
|
{ |
326 |
0 |
VSC_lck_Destroy(sg); |
327 |
0 |
} |
328 |
|
|
329 |
|
#define LOCK(nam) struct VSC_lck *lck_##nam; |
330 |
|
#include "tbl/locks.h" |
331 |
|
|
332 |
|
void |
333 |
36676 |
LCK_Init(void) |
334 |
|
{ |
335 |
|
|
336 |
|
#define LOCK(nam) lck_##nam = Lck_CreateClass(NULL, #nam); |
337 |
|
#include "tbl/locks.h" |
338 |
|
} |