1 #ifndef HARDWARE_ATOMIC_H 2 #define HARDWARE_ATOMIC_H 3 4 /* 5 useful inlines for atomic operations 6 7 Copyright © 2007-2018 The MorphOS Development Team, All Rights Reserved. 8 */ 9 10 #ifndef EXEC_TYPES_H 11 # include <exec/types.h> 12 #endif 13 14 #ifndef DOS_DOS_H 15 # include <dos/dos.h> 16 #endif 17 18 #if defined(__GNUC__) 19 20 #if !defined(__PPC__) 21 # warning "Unsupported CPU family" 22 #endif 23 24 /* 25 * NOTES 26 * 27 * - The pointer to access must be aligned by 4. Unaligned access will 28 * result in exception. 29 * - To work correctly, all concurrent write accesses to the pointer must 30 * be done using ATOMIC-routines. 31 * - ATOMIC_SPINLOCK lock should not be held for a long time. Also, 32 * code keeping the lock most of the time will be equally bad. In fact, 33 * for such cases you should think twice whether SignalSemaphore would 34 * do a better job. 35 * - ATOMIC_SPINLOCK does not nest. 36 */ 37 38 static inline LONG _ATOMIC_FETCH(LONG *ptr) 39 { 40 return *ptr; 41 } 42 #define ATOMIC_FETCH(p) _ATOMIC_FETCH(p) 43 44 static inline LONG _ATOMIC_STORE(LONG *ptr, LONG newval) 45 { 46 register LONG ret; 47 48 __asm__ __volatile__ ( 49 "\n" 50 ".atomic_store_loop_%=:\n" 51 " lwarx %0,0,%1\n" 52 " stwcx. %2,0,%1\n" 53 " bne- .atomic_store_loop_%=\n" 54 : "=&r" (ret) 55 : "b" (ptr), "r" (newval) 56 : "memory", "cr0"); 57 58 return ret; 59 } 60 #define ATOMIC_STORE(p,n) _ATOMIC_STORE(p,n) 61 62 static inline LONG _ATOMIC_ADD(LONG *ptr, LONG val) 63 { 64 register LONG ret, tmp; 65 66 __asm__ __volatile__ ( 67 "\n" 68 ".atomic_add_loop_%=:\n" 69 " lwarx %0,0,%2\n" 70 " add %1,%3,%0\n" 71 " stwcx. %1,0,%2\n" 72 " bne- .atomic_add_loop_%=\n" 73 : "=&b" (ret), "=&r" (tmp) 74 : "b" (ptr), "r" (val) 75 : "memory", "cr0"); 76 77 return ret; 78 } 79 #define ATOMIC_ADD(p,v) _ATOMIC_ADD(p,v) 80 #define ATOMIC_SUB(p,v) _ATOMIC_ADD(p,-(v)) 81 82 static inline ULONG _ATOMIC_OR(ULONG *ptr, ULONG val) 83 { 84 register ULONG ret, tmp; 85 86 __asm__ __volatile__ ( 87 "\n" 88 ".atomic_or_loop_%=:\n" 89 " lwarx %0,0,%2\n" 90 " or %1,%3,%0\n" 91 " stwcx. %1,0,%2\n" 92 " bne- .atomic_or_loop_%=\n" 93 : "=&r" (ret), "=&r" (tmp) 94 : "b" (ptr), "r" (val) 95 : "memory", "cr0"); 96 97 return ret; 98 } 99 #define ATOMIC_OR(p,v) _ATOMIC_OR(p,v) 100 101 static inline ULONG _ATOMIC_AND(ULONG *ptr, ULONG val) 102 { 103 register ULONG ret, tmp; 104 105 __asm__ __volatile__ ( 106 "\n" 107 ".atomic_and_loop_%=:\n" 108 " lwarx %0,0,%2\n" 109 " and %1,%3,%0\n" 110 " stwcx. %1,0,%2\n" 111 " bne- .atomic_and_loop_%=\n" 112 : "=&r" (ret), "=&r" (tmp) 113 : "b" (ptr), "r" (val) 114 : "memory", "cr0"); 115 116 return ret; 117 } 118 #define ATOMIC_AND(p,v) _ATOMIC_AND(p,v) 119 120 static inline ULONG _ATOMIC_XOR(ULONG *ptr, ULONG val) 121 { 122 register ULONG ret, tmp; 123 124 __asm__ __volatile__ ( 125 "\n" 126 ".atomic_xor_loop_%=:\n" 127 " lwarx %0,0,%2\n" 128 " xor %1,%3,%0\n" 129 " stwcx. %1,0,%2\n" 130 " bne- .atomic_xor_loop_%=\n" 131 : "=&r" (ret), "=&r" (tmp) 132 : "b" (ptr), "r" (val) 133 : "memory", "cr0"); 134 135 return ret; 136 } 137 #define ATOMIC_XOR(p,v) _ATOMIC_XOR(p,v) 138 139 static inline LONG _ATOMIC_CMPXCHG(LONG *ptr, LONG old, LONG newval) 140 { 141 register LONG ret; 142 143 __asm__ __volatile__ ( 144 "\n" 145 ".atomic_cmpxchg_loop_%=:\n" 146 " lwarx %0,0,%2\n" 147 " cmpw 0,%0,%3\n" 148 " bne- .atomic_cmpxchg_out_%=\n" 149 " stwcx. %4,0,%2\n" 150 " bne- .atomic_cmpxchg_loop_%=\n" 151 ".atomic_cmpxchg_out_%=:\n" 152 : "=&r" (ret) 153 #if __GNUC__ == 3 154 : "+m" (*ptr), 155 #else 156 : "m" (*ptr), 157 #endif 158 "b" (ptr), "r" (old), "r" (newval) 159 : "memory", "cr0"); 160 161 return ret; 162 } 163 #define ATOMIC_CMPXCHG(p,o,n) _ATOMIC_CMPXCHG(p,o,n) 164 165 166 static inline ULONG _ATOMIC_BTST(ULONG *ptr, LONG bit) 167 { 168 const ULONG mask = 1 << bit; 169 return _ATOMIC_FETCH((LONG *)ptr) & mask; 170 } 171 #define ATOMIC_BTST(p,b) _ATOMIC_BTST(p,b) 172 173 static inline ULONG _ATOMIC_BSET(ULONG *ptr, LONG bit) 174 { 175 const ULONG mask = 1 << bit; 176 return _ATOMIC_OR(ptr, mask) & mask; 177 } 178 #define ATOMIC_BSET(p,b) _ATOMIC_BSET(p,b) 179 180 static inline ULONG _ATOMIC_BCLR(ULONG *ptr, LONG bit) 181 { 182 const ULONG mask = 1 << bit; 183 return _ATOMIC_AND(ptr, ~mask) & mask; 184 } 185 #define ATOMIC_BCLR(p,b) _ATOMIC_BCLR(p,b) 186 187 static inline ULONG _ATOMIC_BCHG(ULONG *ptr, LONG bit) 188 { 189 const ULONG mask = 1 << bit; 190 return _ATOMIC_XOR(ptr, mask) & mask; 191 } 192 #define ATOMIC_BCHG(p,b) _ATOMIC_BCHG(p,b) 193 194 /* 195 * Note: The succ pointer must be the first entry in node. 196 */ 197 static inline void _ATOMIC_SADDHEAD(APTR *head, APTR node) 198 { 199 register APTR tmp; 200 201 __asm__ __volatile__ ( 202 "\n" 203 ".atomic_saddhead_loop_%=:\n" 204 " lwarx %0,0,%1\n" 205 " stw %0,0(%2)\n" 206 " sync\n" 207 " stwcx. %2,0,%1\n" 208 " bne- .atomic_saddhead_loop_%=\n" 209 : "=&r" (tmp) 210 : "b" (head), "b" (node) 211 : "memory", "cr0"); 212 } 213 #define ATOMIC_SADDHEAD(h,n) _ATOMIC_SADDHEAD(h,n) 214 215 static inline APTR _ATOMIC_SREMHEAD(APTR *head) 216 { 217 register APTR node, tmp; 218 219 __asm__ __volatile__ ( 220 "\n" 221 ".atomic_sremhead_loop_%=:\n" 222 " lwarx %0,0,%2\n" 223 " mr. %1,%0\n" 224 " beq- .atomic_sremhead_skip_%=\n" 225 " lwz %1,0(%0)\n" 226 ".atomic_sremhead_skip_%=:\n" 227 " stwcx. %1,0,%2\n" 228 " bne- .atomic_sremhead_loop_%=\n" 229 " isync\n" 230 : "=&b" (node), "=&r" (tmp) 231 : "b" (head) 232 : "memory", "cr0"); 233 234 return node; 235 } 236 #define ATOMIC_SREMHEAD(h) _ATOMIC_SREMHEAD(h) 237 238 /* 239 * Note: The succ bptr pointer must be the first entry in node. 240 */ 241 static inline void _ATOMIC_BADDHEAD(BPTR *head, APTR node) 242 { 243 register BPTR tmp; 244 245 __asm__ __volatile__ ( 246 "\n" 247 ".atomic_baddhead_loop_%=:\n" 248 " lwarx %0,0,%1\n" 249 " stw %0,0(%2)\n" 250 " sync\n" 251 " stwcx. %3,0,%1\n" 252 " bne- .atomic_baddhead_loop_%=\n" 253 : "=&r" (tmp) 254 : "b" (head), "b" (node), "r" ((BPTR) node >> 2) 255 : "memory", "cr0"); 256 } 257 #define ATOMIC_BADDHEAD(h,n) _ATOMIC_BADDHEAD(h,n) 258 259 static inline APTR _ATOMIC_BREMHEAD(BPTR *head) 260 { 261 register BPTR tmp; 262 register APTR node; 263 264 __asm__ __volatile__ ( 265 "\n" 266 ".atomic_bremhead_loop_%=:\n" 267 " lwarx %0,0,%2\n" 268 " rlwinm. %1,%0,2,0,29\n" 269 " beq- .atomic_bremhead_skip_%=\n" 270 " lwz %0,0(%1)\n" 271 ".atomic_bremhead_skip_%=:\n" 272 " stwcx. %0,0,%2\n" 273 " bne- .atomic_bremhead_loop_%=\n" 274 " isync\n" 275 : "=&r" (tmp), "=&b" (node) 276 : "b" (head) 277 : "memory", "cr0"); 278 279 return node; 280 } 281 #define ATOMIC_BREMHEAD(h) _ATOMIC_BREMHEAD(h) 282 283 typedef struct 284 { 285 volatile ULONG as_lock; 286 } atomic_spinlock; 287 288 static inline void _ATOMIC_SPINLOCK(atomic_spinlock *as) 289 { 290 register LONG tmp; 291 292 __asm__ __volatile__ ( 293 "\n" 294 ".atomic_spinlock_loop_%=:\n" 295 " lwarx %0,0,%1\n" 296 " cmpwi %0,0\n" 297 " bne- .atomic_spinlock_loop_%=\n" 298 " stwcx. %2,0,%1\n" 299 " bne- .atomic_spinlock_loop_%=\n" 300 " isync\n" 301 : "=&r" (tmp) 302 : "b" (&as->as_lock), "r" (1) 303 : "memory", "cr0"); 304 } 305 #define ATOMIC_SPINLOCK(p) _ATOMIC_SPINLOCK(p) 306 307 static inline void _ATOMIC_SPINUNLOCK(atomic_spinlock *as) 308 { 309 __asm__ __volatile__ ( 310 "\n" 311 " eieio\n" /* was: sync */ 312 " stw %1,0(%0)\n" 313 : /* no result */ 314 : "b" (&as->as_lock), "r" (0) 315 : "memory"); 316 } 317 #define ATOMIC_SPINUNLOCK(p) _ATOMIC_SPINUNLOCK(p) 318 319 static inline LONG _ATOMIC_SPINTRYLOCK(atomic_spinlock *as) 320 { 321 return _ATOMIC_STORE((LONG *) &as->as_lock, 1) == 0; 322 } 323 #define ATOMIC_SPINTRYLOCK(p) _ATOMIC_SPINTRYLOCK(p) 324 325 #else 326 # warning "Unsupported compiler" 327 #endif 328 329 #endif /* HARDWARE_ATOMIC_H */