1 | /* pthread_spin_lock -- lock a spin lock. Generic version. |
2 | Copyright (C) 2012-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 <atomic.h> |
20 | #include "pthreadP.h" |
21 | #include <shlib-compat.h> |
22 | |
23 | int |
24 | __pthread_spin_lock (pthread_spinlock_t *lock) |
25 | { |
26 | int val = 0; |
27 | |
28 | /* We assume that the first try mostly will be successful, thus we use |
29 | atomic_exchange if it is not implemented by a CAS loop (we also assume |
30 | that atomic_exchange can be faster if it succeeds, see |
31 | ATOMIC_EXCHANGE_USES_CAS). Otherwise, we use a weak CAS and not an |
32 | exchange so we bail out after the first failed attempt to change the |
33 | state. For the subsequent attempts we use atomic_compare_and_exchange |
34 | after we observe that the lock is not acquired. |
35 | See also comment in pthread_spin_trylock. |
36 | We use acquire MO to synchronize-with the release MO store in |
37 | pthread_spin_unlock, and thus ensure that prior critical sections |
38 | happen-before this critical section. */ |
39 | #if ! ATOMIC_EXCHANGE_USES_CAS |
40 | /* Try to acquire the lock with an exchange instruction as this architecture |
41 | has such an instruction and we assume it is faster than a CAS. |
42 | The acquisition succeeds if the lock is not in an acquired state. */ |
43 | if (__glibc_likely (atomic_exchange_acquire (lock, 1) == 0)) |
44 | return 0; |
45 | #else |
46 | /* Try to acquire the lock with a CAS instruction as this architecture |
47 | has no exchange instruction. The acquisition succeeds if the lock is not |
48 | acquired. */ |
49 | if (__glibc_likely (atomic_compare_exchange_weak_acquire (lock, &val, 1))) |
50 | return 0; |
51 | #endif |
52 | |
53 | do |
54 | { |
55 | /* The lock is contended and we need to wait. Going straight back |
56 | to cmpxchg is not a good idea on many targets as that will force |
57 | expensive memory synchronizations among processors and penalize other |
58 | running threads. |
59 | There is no technical reason for throwing in a CAS every now and then, |
60 | and so far we have no evidence that it can improve performance. |
61 | If that would be the case, we have to adjust other spin-waiting loops |
62 | elsewhere, too! |
63 | Thus we use relaxed MO reads until we observe the lock to not be |
64 | acquired anymore. */ |
65 | do |
66 | { |
67 | /* TODO Back-off. */ |
68 | |
69 | atomic_spin_nop (); |
70 | |
71 | val = atomic_load_relaxed (lock); |
72 | } |
73 | while (val != 0); |
74 | |
75 | /* We need acquire memory order here for the same reason as mentioned |
76 | for the first try to lock the spinlock. */ |
77 | } |
78 | while (!atomic_compare_exchange_weak_acquire (lock, &val, 1)); |
79 | |
80 | return 0; |
81 | } |
82 | versioned_symbol (libc, __pthread_spin_lock, pthread_spin_lock, GLIBC_2_34); |
83 | |
84 | #if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_2, GLIBC_2_34) |
85 | compat_symbol (libpthread, __pthread_spin_lock, pthread_spin_lock, GLIBC_2_2); |
86 | #endif |
87 | |