1 /* plock - progressive locks 2 * 3 * Copyright (C) 2012-2017 Willy Tarreau <w@1wt.eu> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be 14 * included in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 * OTHER DEALINGS IN THE SOFTWARE. 24 */ 25 26 #include "atomic-ops.h" 27 28 /* 64 bit */ 29 #define PLOCK64_RL_1 0x0000000000000004ULL 30 #define PLOCK64_RL_ANY 0x00000000FFFFFFFCULL 31 #define PLOCK64_SL_1 0x0000000100000000ULL 32 #define PLOCK64_SL_ANY 0x0000000300000000ULL 33 #define PLOCK64_WL_1 0x0000000400000000ULL 34 #define PLOCK64_WL_ANY 0xFFFFFFFC00000000ULL 35 36 /* 32 bit */ 37 #define PLOCK32_RL_1 0x00000004 38 #define PLOCK32_RL_ANY 0x0000FFFC 39 #define PLOCK32_SL_1 0x00010000 40 #define PLOCK32_SL_ANY 0x00030000 41 #define PLOCK32_WL_1 0x00040000 42 #define PLOCK32_WL_ANY 0xFFFC0000 43 44 /* dereferences <*p> as unsigned long without causing aliasing issues */ 45 #define pl_deref_long(p) ({ volatile unsigned long *__pl_l = (void *)(p); *__pl_l; }) 46 47 /* dereferences <*p> as unsigned int without causing aliasing issues */ 48 #define pl_deref_int(p) ({ volatile unsigned int *__pl_i = (void *)(p); *__pl_i; }) 49 50 /* request shared read access (R), return non-zero on success, otherwise 0 */ 51 #define pl_try_r(lock) ( \ 52 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 53 unsigned long __pl_r = pl_deref_long(lock) & PLOCK64_WL_ANY; \ 54 pl_barrier(); \ 55 if (!__builtin_expect(__pl_r, 0)) { \ 56 __pl_r = pl_xadd((lock), PLOCK64_RL_1) & PLOCK64_WL_ANY; \ 57 if (__builtin_expect(__pl_r, 0)) \ 58 pl_sub((lock), PLOCK64_RL_1); \ 59 } \ 60 !__pl_r; /* return value */ \ 61 }) : (sizeof(*(lock)) == 4) ? ({ \ 62 unsigned int __pl_r = pl_deref_int(lock) & PLOCK32_WL_ANY; \ 63 pl_barrier(); \ 64 if (!__builtin_expect(__pl_r, 0)) { \ 65 __pl_r = pl_xadd((lock), PLOCK32_RL_1) & PLOCK32_WL_ANY; \ 66 if (__builtin_expect(__pl_r, 0)) \ 67 pl_sub((lock), PLOCK32_RL_1); \ 68 } \ 69 !__pl_r; /* return value */ \ 70 }) : ({ \ 71 void __unsupported_argument_size_for_pl_try_r__(char *,int); \ 72 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 73 __unsupported_argument_size_for_pl_try_r__(__FILE__,__LINE__); \ 74 0; \ 75 }) \ 76 ) 77 78 /* request shared read access (R) and wait for it */ 79 #define pl_take_r(lock) \ 80 do { \ 81 while (__builtin_expect(pl_try_r(lock), 1) == 0) \ 82 pl_cpu_relax(); \ 83 } while (0) 84 85 /* release the read access (R) lock */ 86 #define pl_drop_r(lock) ( \ 87 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 88 pl_sub(lock, PLOCK64_RL_1); \ 89 }) : (sizeof(*(lock)) == 4) ? ({ \ 90 pl_sub(lock, PLOCK32_RL_1); \ 91 }) : ({ \ 92 void __unsupported_argument_size_for_pl_drop_r__(char *,int); \ 93 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 94 __unsupported_argument_size_for_pl_drop_r__(__FILE__,__LINE__); \ 95 }) \ 96 ) 97 98 /* request a seek access (S), return non-zero on success, otherwise 0 */ 99 #define pl_try_s(lock) ( \ 100 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 101 unsigned long __pl_r = pl_deref_long(lock); \ 102 pl_barrier(); \ 103 if (!__builtin_expect(__pl_r & (PLOCK64_WL_ANY | PLOCK64_SL_ANY), 0)) { \ 104 __pl_r = pl_xadd((lock), PLOCK64_SL_1 | PLOCK64_RL_1) & \ 105 (PLOCK64_WL_ANY | PLOCK64_SL_ANY); \ 106 if (__builtin_expect(__pl_r, 0)) \ 107 pl_sub((lock), PLOCK64_SL_1 | PLOCK64_RL_1); \ 108 } \ 109 !__pl_r; /* return value */ \ 110 }) : (sizeof(*(lock)) == 4) ? ({ \ 111 unsigned int __pl_r = pl_deref_int(lock); \ 112 pl_barrier(); \ 113 if (!__builtin_expect(__pl_r & (PLOCK32_WL_ANY | PLOCK32_SL_ANY), 0)) { \ 114 __pl_r = pl_xadd((lock), PLOCK32_SL_1 | PLOCK32_RL_1) & \ 115 (PLOCK32_WL_ANY | PLOCK32_SL_ANY); \ 116 if (__builtin_expect(__pl_r, 0)) \ 117 pl_sub((lock), PLOCK32_SL_1 | PLOCK32_RL_1); \ 118 } \ 119 !__pl_r; /* return value */ \ 120 }) : ({ \ 121 void __unsupported_argument_size_for_pl_try_s__(char *,int); \ 122 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 123 __unsupported_argument_size_for_pl_try_s__(__FILE__,__LINE__); \ 124 0; \ 125 }) \ 126 ) 127 128 /* request a seek access (S) and wait for it */ 129 #define pl_take_s(lock) \ 130 do { \ 131 while (__builtin_expect(pl_try_s(lock), 0) == 0) \ 132 pl_cpu_relax(); \ 133 } while (0) 134 135 /* release the seek access (S) lock */ 136 #define pl_drop_s(lock) ( \ 137 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 138 pl_sub(lock, PLOCK64_SL_1 + PLOCK64_RL_1); \ 139 }) : (sizeof(*(lock)) == 4) ? ({ \ 140 pl_sub(lock, PLOCK32_SL_1 + PLOCK32_RL_1); \ 141 }) : ({ \ 142 void __unsupported_argument_size_for_pl_drop_s__(char *,int); \ 143 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 144 __unsupported_argument_size_for_pl_drop_s__(__FILE__,__LINE__); \ 145 }) \ 146 ) 147 148 /* drop the S lock and go back to the R lock */ 149 #define pl_stor(lock) ( \ 150 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 151 pl_sub(lock, PLOCK64_SL_1); \ 152 }) : (sizeof(*(lock)) == 4) ? ({ \ 153 pl_sub(lock, PLOCK32_SL_1); \ 154 }) : ({ \ 155 void __unsupported_argument_size_for_pl_stor__(char *,int); \ 156 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 157 __unsupported_argument_size_for_pl_stor__(__FILE__,__LINE__); \ 158 }) \ 159 ) 160 161 /* take the W lock under the S lock */ 162 #define pl_stow(lock) ( \ 163 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 164 unsigned long __pl_r = pl_xadd((lock), PLOCK64_WL_1); \ 165 pl_barrier(); \ 166 while ((__pl_r & PLOCK64_RL_ANY) != PLOCK64_RL_1) \ 167 __pl_r = pl_deref_long(lock); \ 168 }) : (sizeof(*(lock)) == 4) ? ({ \ 169 unsigned int __pl_r = pl_xadd((lock), PLOCK32_WL_1); \ 170 pl_barrier(); \ 171 while ((__pl_r & PLOCK32_RL_ANY) != PLOCK32_RL_1) \ 172 __pl_r = pl_deref_int(lock); \ 173 }) : ({ \ 174 void __unsupported_argument_size_for_pl_stow__(char *,int); \ 175 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 176 __unsupported_argument_size_for_pl_stow__(__FILE__,__LINE__); \ 177 }) \ 178 ) 179 180 /* drop the W lock and go back to the S lock */ 181 #define pl_wtos(lock) ( \ 182 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 183 pl_sub(lock, PLOCK64_WL_1); \ 184 }) : (sizeof(*(lock)) == 4) ? ({ \ 185 pl_sub(lock, PLOCK32_WL_1); \ 186 }) : ({ \ 187 void __unsupported_argument_size_for_pl_wtos__(char *,int); \ 188 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 189 __unsupported_argument_size_for_pl_wtos__(__FILE__,__LINE__); \ 190 }) \ 191 ) 192 193 /* drop the W lock and go back to the R lock */ 194 #define pl_wtor(lock) ( \ 195 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 196 pl_sub(lock, PLOCK64_WL_1 | PLOCK64_SL_1); \ 197 }) : (sizeof(*(lock)) == 4) ? ({ \ 198 pl_sub(lock, PLOCK32_WL_1 | PLOCK32_SL_1); \ 199 }) : ({ \ 200 void __unsupported_argument_size_for_pl_wtor__(char *,int); \ 201 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 202 __unsupported_argument_size_for_pl_wtor__(__FILE__,__LINE__); \ 203 }) \ 204 ) 205 206 /* request a write access (W), return non-zero on success, otherwise 0. 207 * 208 * Below there is something important : by taking both W and S, we will cause 209 * an overflow of W at 4/5 of the maximum value that can be stored into W due 210 * to the fact that S is 2 bits, so we're effectively adding 5 to the word 211 * composed by W:S. But for all words multiple of 4 bits, the maximum value is 212 * multiple of 15 thus of 5. So the largest value we can store with all bits 213 * set to one will be met by adding 5, and then adding 5 again will place value 214 * 1 in W and value 0 in S, so we never leave W with 0. Also, even upon such an 215 * overflow, there's no risk to confuse it with an atomic lock because R is not 216 * null since it will not have overflown. For 32-bit locks, this situation 217 * happens when exactly 13108 threads try to grab the lock at once, W=1, S=0 218 * and R=13108. For 64-bit locks, it happens at 858993460 concurrent writers 219 * where W=1, S=0 and R=858993460. 220 */ 221 #define pl_try_w(lock) ( \ 222 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 223 unsigned long __pl_r = pl_deref_long(lock); \ 224 pl_barrier(); \ 225 if (!__builtin_expect(__pl_r & (PLOCK64_WL_ANY | PLOCK64_SL_ANY), 0)) { \ 226 __pl_r = pl_xadd((lock), PLOCK64_WL_1 | PLOCK64_SL_1 | PLOCK64_RL_1); \ 227 if (__builtin_expect(__pl_r & (PLOCK64_WL_ANY | PLOCK64_SL_ANY), 0)) { \ 228 /* a writer, seeker or atomic is present, let's leave */ \ 229 pl_sub((lock), PLOCK64_WL_1 | PLOCK64_SL_1 | PLOCK64_RL_1); \ 230 __pl_r &= (PLOCK64_WL_ANY | PLOCK64_SL_ANY); /* return value */\ 231 } else { \ 232 /* wait for all other readers to leave */ \ 233 while (__pl_r) \ 234 __pl_r = pl_deref_long(lock) - \ 235 (PLOCK64_WL_1 | PLOCK64_SL_1 | PLOCK64_RL_1); \ 236 __pl_r = 0; \ 237 } \ 238 } \ 239 !__pl_r; /* return value */ \ 240 }) : (sizeof(*(lock)) == 4) ? ({ \ 241 unsigned int __pl_r = pl_deref_int(lock); \ 242 pl_barrier(); \ 243 if (!__builtin_expect(__pl_r & (PLOCK32_WL_ANY | PLOCK32_SL_ANY), 0)) { \ 244 __pl_r = pl_xadd((lock), PLOCK32_WL_1 | PLOCK32_SL_1 | PLOCK32_RL_1); \ 245 if (__builtin_expect(__pl_r & (PLOCK32_WL_ANY | PLOCK32_SL_ANY), 0)) { \ 246 /* a writer, seeker or atomic is present, let's leave */ \ 247 pl_sub((lock), PLOCK32_WL_1 | PLOCK32_SL_1 | PLOCK32_RL_1); \ 248 __pl_r &= (PLOCK32_WL_ANY | PLOCK32_SL_ANY); /* return value */\ 249 } else { \ 250 /* wait for all other readers to leave */ \ 251 while (__pl_r) \ 252 __pl_r = pl_deref_int(lock) - \ 253 (PLOCK32_WL_1 | PLOCK32_SL_1 | PLOCK32_RL_1); \ 254 __pl_r = 0; \ 255 } \ 256 } \ 257 !__pl_r; /* return value */ \ 258 }) : ({ \ 259 void __unsupported_argument_size_for_pl_try_w__(char *,int); \ 260 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 261 __unsupported_argument_size_for_pl_try_w__(__FILE__,__LINE__); \ 262 0; \ 263 }) \ 264 ) 265 266 /* request a seek access (W) and wait for it */ 267 #define pl_take_w(lock) \ 268 do { \ 269 while (__builtin_expect(pl_try_w(lock), 0) == 0) \ 270 pl_cpu_relax(); \ 271 } while (0) 272 273 /* drop the write (W) lock entirely */ 274 #define pl_drop_w(lock) ( \ 275 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 276 pl_sub(lock, PLOCK64_WL_1 | PLOCK64_SL_1 | PLOCK64_RL_1); \ 277 }) : (sizeof(*(lock)) == 4) ? ({ \ 278 pl_sub(lock, PLOCK32_WL_1 | PLOCK32_SL_1 | PLOCK32_RL_1); \ 279 }) : ({ \ 280 void __unsupported_argument_size_for_pl_drop_w__(char *,int); \ 281 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 282 __unsupported_argument_size_for_pl_drop_w__(__FILE__,__LINE__); \ 283 }) \ 284 ) 285 286 /* Try to upgrade from R to S, return non-zero on success, otherwise 0. 287 * This lock will fail if S or W are already held. In case of failure to grab 288 * the lock, it MUST NOT be retried without first dropping R, or it may never 289 * complete due to S waiting for R to leave before upgrading to W. 290 */ 291 #define pl_try_rtos(lock) ( \ 292 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 293 unsigned long __pl_r = pl_deref_long(lock); \ 294 pl_barrier(); \ 295 if (!__builtin_expect(__pl_r & (PLOCK64_WL_ANY | PLOCK64_SL_ANY), 0)) { \ 296 __pl_r = pl_xadd((lock), PLOCK64_SL_1) & \ 297 (PLOCK64_WL_ANY | PLOCK64_SL_ANY); \ 298 if (__builtin_expect(__pl_r, 0)) \ 299 pl_sub((lock), PLOCK64_SL_1); \ 300 } \ 301 !__pl_r; /* return value */ \ 302 }) : (sizeof(*(lock)) == 4) ? ({ \ 303 unsigned int __pl_r = pl_deref_int(lock); \ 304 pl_barrier(); \ 305 if (!__builtin_expect(__pl_r & (PLOCK32_WL_ANY | PLOCK32_SL_ANY), 0)) { \ 306 __pl_r = pl_xadd((lock), PLOCK32_SL_1) & \ 307 (PLOCK32_WL_ANY | PLOCK32_SL_ANY); \ 308 if (__builtin_expect(__pl_r, 0)) \ 309 pl_sub((lock), PLOCK32_SL_1); \ 310 } \ 311 !__pl_r; /* return value */ \ 312 }) : ({ \ 313 void __unsupported_argument_size_for_pl_try_rtos__(char *,int); \ 314 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 315 __unsupported_argument_size_for_pl_try_rtos__(__FILE__,__LINE__); \ 316 0; \ 317 }) \ 318 ) 319 320 321 /* request atomic write access (A), return non-zero on success, otherwise 0. 322 * It's a bit tricky as we only use the W bits for this and want to distinguish 323 * between other atomic users and regular lock users. We have to give up if an 324 * S lock appears. It's possible that such a lock stays hidden in the W bits 325 * after an overflow, but in this case R is still held, ensuring we stay in the 326 * loop until we discover the conflict. The lock only return successfully if all 327 * readers are gone (or converted to A). 328 */ 329 #define pl_try_a(lock) ( \ 330 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 331 unsigned long __pl_r = pl_deref_long(lock) & PLOCK64_SL_ANY; \ 332 pl_barrier(); \ 333 if (!__builtin_expect(__pl_r, 0)) { \ 334 __pl_r = pl_xadd((lock), PLOCK64_WL_1); \ 335 while (1) { \ 336 if (__builtin_expect(__pl_r & PLOCK64_SL_ANY, 0)) { \ 337 pl_sub((lock), PLOCK64_WL_1); \ 338 break; /* return !__pl_r */ \ 339 } \ 340 __pl_r &= PLOCK64_RL_ANY; \ 341 if (!__builtin_expect(__pl_r, 0)) \ 342 break; /* return !__pl_r */ \ 343 __pl_r = pl_deref_long(lock); \ 344 } \ 345 } \ 346 !__pl_r; /* return value */ \ 347 }) : (sizeof(*(lock)) == 4) ? ({ \ 348 unsigned int __pl_r = pl_deref_int(lock) & PLOCK32_SL_ANY; \ 349 pl_barrier(); \ 350 if (!__builtin_expect(__pl_r, 0)) { \ 351 __pl_r = pl_xadd((lock), PLOCK32_WL_1); \ 352 while (1) { \ 353 if (__builtin_expect(__pl_r & PLOCK32_SL_ANY, 0)) { \ 354 pl_sub((lock), PLOCK32_WL_1); \ 355 break; /* return !__pl_r */ \ 356 } \ 357 __pl_r &= PLOCK32_RL_ANY; \ 358 if (!__builtin_expect(__pl_r, 0)) \ 359 break; /* return !__pl_r */ \ 360 __pl_r = pl_deref_int(lock); \ 361 } \ 362 } \ 363 !__pl_r; /* return value */ \ 364 }) : ({ \ 365 void __unsupported_argument_size_for_pl_try_a__(char *,int); \ 366 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 367 __unsupported_argument_size_for_pl_try_a__(__FILE__,__LINE__); \ 368 0; \ 369 }) \ 370 ) 371 372 /* request atomic write access (A) and wait for it */ 373 #define pl_take_a(lock) \ 374 do { \ 375 while (__builtin_expect(pl_try_a(lock), 1) == 0) \ 376 pl_cpu_relax(); \ 377 } while (0) 378 379 /* release atomic write access (A) lock */ 380 #define pl_drop_a(lock) ( \ 381 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 382 pl_sub(lock, PLOCK64_WL_1); \ 383 }) : (sizeof(*(lock)) == 4) ? ({ \ 384 pl_sub(lock, PLOCK32_WL_1); \ 385 }) : ({ \ 386 void __unsupported_argument_size_for_pl_drop_a__(char *,int); \ 387 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 388 __unsupported_argument_size_for_pl_drop_a__(__FILE__,__LINE__); \ 389 }) \ 390 ) 391 392 /* Try to upgrade from R to A, return non-zero on success, otherwise 0. 393 * This lock will fail if S is held or appears while waiting (typically due to 394 * a previous grab that was disguised as a W due to an overflow). In case of 395 * failure to grab the lock, it MUST NOT be retried without first dropping R, 396 * or it may never complete due to S waiting for R to leave before upgrading 397 * to W. The lock succeeds once there's no more R (ie all of them have either 398 * completed or were turned to A). 399 */ 400 #define pl_try_rtoa(lock) ( \ 401 (sizeof(long) == 8 && sizeof(*(lock)) == 8) ? ({ \ 402 unsigned long __pl_r = pl_deref_long(lock) & PLOCK64_SL_ANY; \ 403 pl_barrier(); \ 404 if (!__builtin_expect(__pl_r, 0)) { \ 405 __pl_r = pl_xadd((lock), PLOCK64_WL_1 - PLOCK64_RL_1); \ 406 while (1) { \ 407 if (__builtin_expect(__pl_r & PLOCK64_SL_ANY, 0)) { \ 408 pl_sub((lock), PLOCK64_WL_1 - PLOCK64_RL_1); \ 409 break; /* return !__pl_r */ \ 410 } \ 411 __pl_r &= PLOCK64_RL_ANY; \ 412 if (!__builtin_expect(__pl_r, 0)) \ 413 break; /* return !__pl_r */ \ 414 __pl_r = pl_deref_long(lock); \ 415 } \ 416 } \ 417 !__pl_r; /* return value */ \ 418 }) : (sizeof(*(lock)) == 4) ? ({ \ 419 unsigned int __pl_r = pl_deref_int(lock) & PLOCK32_SL_ANY; \ 420 pl_barrier(); \ 421 if (!__builtin_expect(__pl_r, 0)) { \ 422 __pl_r = pl_xadd((lock), PLOCK32_WL_1 - PLOCK32_RL_1); \ 423 while (1) { \ 424 if (__builtin_expect(__pl_r & PLOCK32_SL_ANY, 0)) { \ 425 pl_sub((lock), PLOCK32_WL_1 - PLOCK32_RL_1); \ 426 break; /* return !__pl_r */ \ 427 } \ 428 __pl_r &= PLOCK32_RL_ANY; \ 429 if (!__builtin_expect(__pl_r, 0)) \ 430 break; /* return !__pl_r */ \ 431 __pl_r = pl_deref_int(lock); \ 432 } \ 433 } \ 434 !__pl_r; /* return value */ \ 435 }) : ({ \ 436 void __unsupported_argument_size_for_pl_try_rtoa__(char *,int); \ 437 if (sizeof(*(lock)) != 4 && (sizeof(long) != 8 || sizeof(*(lock)) != 8)) \ 438 __unsupported_argument_size_for_pl_try_rtoa__(__FILE__,__LINE__); \ 439 0; \ 440 }) \ 441 ) 442