yuezonghe | 824eb0c | 2024-06-27 02:32:26 -0700 | [diff] [blame] | 1 | /* Linuxthreads - a simple clone()-based implementation of Posix */ |
| 2 | /* threads for Linux. */ |
| 3 | /* Copyright (C) 1998 Xavier Leroy (Xavier.Leroy@inria.fr) */ |
| 4 | /* */ |
| 5 | /* This program is free software; you can redistribute it and/or */ |
| 6 | /* modify it under the terms of the GNU Library General Public License */ |
| 7 | /* as published by the Free Software Foundation; either version 2 */ |
| 8 | /* of the License, or (at your option) any later version. */ |
| 9 | /* */ |
| 10 | /* This program 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 */ |
| 13 | /* GNU Library General Public License for more details. */ |
| 14 | |
| 15 | #include <bits/initspin.h> |
| 16 | |
| 17 | |
| 18 | /* There are 2 compare and swap synchronization primitives with |
| 19 | different semantics: |
| 20 | |
| 21 | 1. compare_and_swap, which has acquire semantics (i.e. it |
| 22 | completes befor subsequent writes.) |
| 23 | 2. compare_and_swap_with_release_semantics, which has release |
| 24 | semantics (it completes after previous writes.) |
| 25 | |
| 26 | For those platforms on which they are the same. HAS_COMPARE_AND_SWAP |
| 27 | should be defined. For those platforms on which they are different, |
| 28 | HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS has to be defined. */ |
| 29 | |
| 30 | #ifndef HAS_COMPARE_AND_SWAP |
| 31 | #ifdef HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS |
| 32 | #define HAS_COMPARE_AND_SWAP |
| 33 | #endif |
| 34 | #endif |
| 35 | |
| 36 | #if defined(TEST_FOR_COMPARE_AND_SWAP) |
| 37 | |
| 38 | extern int __pthread_has_cas; |
| 39 | extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval, |
| 40 | int * spinlock); |
| 41 | |
| 42 | static __inline__ int compare_and_swap(long * ptr, long oldval, long newval, |
| 43 | int * spinlock) |
| 44 | { |
| 45 | if (__builtin_expect (__pthread_has_cas, 1)) |
| 46 | return __compare_and_swap(ptr, oldval, newval); |
| 47 | else |
| 48 | return __pthread_compare_and_swap(ptr, oldval, newval, spinlock); |
| 49 | } |
| 50 | |
| 51 | #elif defined(HAS_COMPARE_AND_SWAP) |
| 52 | |
| 53 | #ifdef IMPLEMENT_TAS_WITH_CAS |
| 54 | #define testandset(p) !__compare_and_swap((long int *) p, 0, 1) |
| 55 | #endif |
| 56 | |
| 57 | #ifdef HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS |
| 58 | |
| 59 | static __inline__ int |
| 60 | compare_and_swap_with_release_semantics (long * ptr, long oldval, |
| 61 | long newval, int * spinlock) |
| 62 | { |
| 63 | return __compare_and_swap_with_release_semantics (ptr, oldval, |
| 64 | newval); |
| 65 | } |
| 66 | |
| 67 | #endif |
| 68 | |
| 69 | static __inline__ int compare_and_swap(long * ptr, long oldval, long newval, |
| 70 | int * spinlock) |
| 71 | { |
| 72 | return __compare_and_swap(ptr, oldval, newval); |
| 73 | } |
| 74 | |
| 75 | #else |
| 76 | |
| 77 | extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval, |
| 78 | int * spinlock); |
| 79 | |
| 80 | static __inline__ int compare_and_swap(long * ptr, long oldval, long newval, |
| 81 | int * spinlock) |
| 82 | { |
| 83 | return __pthread_compare_and_swap(ptr, oldval, newval, spinlock); |
| 84 | } |
| 85 | |
| 86 | #endif |
| 87 | |
| 88 | #ifndef HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS |
| 89 | #define compare_and_swap_with_release_semantics compare_and_swap |
| 90 | #define __compare_and_swap_with_release_semantics __compare_and_swap |
| 91 | #endif |
| 92 | |
| 93 | /* Internal locks */ |
| 94 | |
| 95 | extern void internal_function __pthread_lock(struct _pthread_fastlock * lock, |
| 96 | pthread_descr self); |
| 97 | extern int __pthread_unlock(struct _pthread_fastlock *lock); |
| 98 | |
| 99 | static __inline__ void __pthread_init_lock(struct _pthread_fastlock * lock) |
| 100 | { |
| 101 | lock->__status = 0; |
| 102 | lock->__spinlock = __LT_SPINLOCK_INIT; |
| 103 | } |
| 104 | |
| 105 | static __inline__ int __pthread_trylock (struct _pthread_fastlock * lock) |
| 106 | { |
| 107 | #if defined TEST_FOR_COMPARE_AND_SWAP |
| 108 | if (!__pthread_has_cas) |
| 109 | #endif |
| 110 | #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP |
| 111 | { |
| 112 | return (testandset(&lock->__spinlock) ? EBUSY : 0); |
| 113 | } |
| 114 | #endif |
| 115 | |
| 116 | #if defined HAS_COMPARE_AND_SWAP |
| 117 | do { |
| 118 | if (lock->__status != 0) return EBUSY; |
| 119 | } while(! __compare_and_swap(&lock->__status, 0, 1)); |
| 120 | return 0; |
| 121 | #endif |
| 122 | } |
| 123 | |
| 124 | /* Variation of internal lock used for pthread_mutex_t, supporting |
| 125 | timed-out waits. Warning: do not mix these operations with the above ones |
| 126 | over the same lock object! */ |
| 127 | |
| 128 | extern void __pthread_alt_lock(struct _pthread_fastlock * lock, |
| 129 | pthread_descr self); |
| 130 | |
| 131 | extern int __pthread_alt_timedlock(struct _pthread_fastlock * lock, |
| 132 | pthread_descr self, const struct timespec *abstime); |
| 133 | |
| 134 | extern void __pthread_alt_unlock(struct _pthread_fastlock *lock); |
| 135 | |
| 136 | static __inline__ void __pthread_alt_init_lock(struct _pthread_fastlock * lock) |
| 137 | { |
| 138 | lock->__status = 0; |
| 139 | lock->__spinlock = __LT_SPINLOCK_INIT; |
| 140 | } |
| 141 | |
| 142 | static __inline__ int __pthread_alt_trylock (struct _pthread_fastlock * lock) |
| 143 | { |
| 144 | #if defined TEST_FOR_COMPARE_AND_SWAP |
| 145 | if (!__pthread_has_cas) |
| 146 | #endif |
| 147 | #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP |
| 148 | { |
| 149 | int res = EBUSY; |
| 150 | |
| 151 | if (testandset(&lock->__spinlock) == 0) |
| 152 | { |
| 153 | if (lock->__status == 0) |
| 154 | { |
| 155 | lock->__status = 1; |
| 156 | WRITE_MEMORY_BARRIER(); |
| 157 | res = 0; |
| 158 | } |
| 159 | lock->__spinlock = __LT_SPINLOCK_INIT; |
| 160 | } |
| 161 | return res; |
| 162 | } |
| 163 | #endif |
| 164 | |
| 165 | #if defined HAS_COMPARE_AND_SWAP |
| 166 | do { |
| 167 | if (lock->__status != 0) return EBUSY; |
| 168 | } while(! compare_and_swap(&lock->__status, 0, 1, &lock->__spinlock)); |
| 169 | return 0; |
| 170 | #endif |
| 171 | } |
| 172 | |
| 173 | /* Operations on pthread_atomic, which is defined in internals.h */ |
| 174 | |
| 175 | static __inline__ long |
| 176 | pthread_atomic_increment (struct pthread_atomic *pa) |
| 177 | { |
| 178 | long oldval; |
| 179 | |
| 180 | do { |
| 181 | oldval = pa->p_count; |
| 182 | } while (!compare_and_swap(&pa->p_count, oldval, oldval + 1, &pa->p_spinlock)); |
| 183 | |
| 184 | return oldval; |
| 185 | } |
| 186 | |
| 187 | |
| 188 | static __inline__ long |
| 189 | pthread_atomic_decrement (struct pthread_atomic *pa) |
| 190 | { |
| 191 | long oldval; |
| 192 | |
| 193 | do { |
| 194 | oldval = pa->p_count; |
| 195 | } while (!compare_and_swap(&pa->p_count, oldval, oldval - 1, &pa->p_spinlock)); |
| 196 | |
| 197 | return oldval; |
| 198 | } |
| 199 | |
| 200 | |
| 201 | static __inline__ __attribute__((always_inline)) void |
| 202 | __pthread_set_own_extricate_if (pthread_descr self, pthread_extricate_if *peif) |
| 203 | { |
| 204 | /* Only store a non-null peif if the thread has cancellation enabled. |
| 205 | Otherwise pthread_cancel will unconditionally call the extricate handler, |
| 206 | and restart the thread giving rise to forbidden spurious wakeups. */ |
| 207 | if (peif == NULL |
| 208 | || THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) |
| 209 | { |
| 210 | /* If we are removing the extricate interface, we need to synchronize |
| 211 | against pthread_cancel so that it does not continue with a pointer |
| 212 | to a deallocated pthread_extricate_if struct! The thread lock |
| 213 | is (ab)used for this synchronization purpose. */ |
| 214 | if (peif == NULL) |
| 215 | __pthread_lock (THREAD_GETMEM(self, p_lock), self); |
| 216 | THREAD_SETMEM(self, p_extricate, peif); |
| 217 | if (peif == NULL) |
| 218 | __pthread_unlock (THREAD_GETMEM(self, p_lock)); |
| 219 | } |
| 220 | } |