2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
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 General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
23 ///////////////////////////////////////////////////////////////////////////
24 // 32-bit atomic compare-and-swap
25 // Returns previous value of *pAddr
26 ///////////////////////////////////////////////////////////////////////////
27 long cas(volatile long *pAddr, long expectedVal, long swapVal)
29 #if defined(HAS_BUILTIN_SYNC_VAL_COMPARE_AND_SWAP)
30 return(__sync_val_compare_and_swap(pAddr, expectedVal, swapVal));
31 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
33 __asm__ __volatile__ (
34 " 1: lwarx %0,0,%2 \n" /* Load the current value of *pAddr(%2) into prev (%0) and lock pAddr, */
35 " cmpw 0,%0,%3 \n" /* Verify that the current value (%2) == old value (%3) */
36 " bne- 2f \n" /* Bail if the two values are not equal [not as expected] */
37 " stwcx. %4,0,%2 \n" /* Attempt to store swapVal (%4) value into *pAddr (%2) [p must still be reserved] */
38 " bne- 1b \n" /* Loop if p was no longer reserved */
39 " isync \n" /* Reconcile multiple processors [if present] */
41 : "=&r" (prev), "+m" (*pAddr) /* Outputs [prev, *pAddr] */
42 : "r" (pAddr), "r" (expectedVal), "r" (swapVal) /* Inputs [pAddr, expectedVal, swapVal] */
43 : "cc", "memory"); /* Clobbers */
46 #elif defined(__arm__)
49 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
51 "ldrex %0, [%1] \n" // Load the current value of *pAddr(%1) into prev (%0) and lock pAddr,
52 "cmp %0, %2 \n" // Verify that the current value (%0) == old value (%2)
53 "bne 2f \n" // Bail if the two values are not equal [not as expected]
54 "strex r1, %3, [%1] \n"
57 "dmb ish \n" // Memory barrier.
60 : "r"(pAddr), "r"(expectedVal),"r"(swapVal)
65 #elif defined(__mips__)
68 #error atomic cas undefined for mips
71 #elif defined(TARGET_WINDOWS)
76 mov eax, expectedVal ;
81 lock cmpxchg dword ptr [ebx], ecx ;
83 // Store the return value
88 #else // Linux / OSX86 (GCC)
90 __asm__ __volatile__ (
93 : "r" (swapVal), "m" (*pAddr), "0" (expectedVal)
100 ///////////////////////////////////////////////////////////////////////////
101 // 64-bit atomic compare-and-swap
102 // Returns previous value of *pAddr
103 ///////////////////////////////////////////////////////////////////////////
104 long long cas2(volatile long long* pAddr, long long expectedVal, long long swapVal)
106 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || defined(__mips__) // PowerPC, ARM, and MIPS
107 // Not available/required
108 // Hack to allow compilation
109 throw "cas2 is not implemented";
111 #elif defined(TARGET_WINDOWS)
116 mov eax, dword ptr [expectedVal] ;
117 mov edx, dword ptr expectedVal[4] ;
118 mov ebx, dword ptr [swapVal] ;
119 mov ecx, dword ptr swapVal[4] ;
120 lock cmpxchg8b qword ptr [esi] ;
121 mov dword ptr [prev], eax ;
122 mov dword ptr prev[4], edx ;
126 #else // Linux / OSX86 (GCC)
127 #if !defined (__x86_64)
130 " push %%ebx \n" // We have to manually handle ebx, because PIC uses it and the compiler refuses to build anything that touches it
132 " lock/cmpxchg8b (%%esi) \n"
135 : "c" ((unsigned long)(swapVal >> 32)), "0" (expectedVal), "S" (pAddr), "m" (swapVal)
139 // Hack to allow compilation on x86_64
140 throw "cas2 is not implemented on x86_64!";
145 ///////////////////////////////////////////////////////////////////////////
146 // 32-bit atomic increment
147 // Returns new value of *pAddr
148 ///////////////////////////////////////////////////////////////////////////
149 long AtomicIncrement(volatile long* pAddr)
151 #if defined(HAS_BUILTIN_SYNC_ADD_AND_FETCH)
152 return __sync_add_and_fetch(pAddr, 1);
154 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
156 __asm__ __volatile__ (
158 "1: lwarx %0, 0, %1 \n"
160 "stwcx. %0, 0, %1 \n"
165 : "cc", "xer", "memory");
168 #elif defined(__arm__) && !defined(__ARM_ARCH_5__)
171 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
173 "ldrex %0, [%1] \n" // (val = *pAddr)
174 "add %0, #1 \n" // (val += 1)
175 "strex r1, %0, [%1] \n"
178 "dmb ish \n" // Memory barrier.
185 #elif defined(__mips__)
188 #error AtomicIncrement undefined for mips
191 #elif defined(TARGET_WINDOWS)
196 lock inc dword ptr [eax] ;
202 #elif defined(__x86_64__)
203 register long result;
204 __asm__ __volatile__ (
206 : "=r" (result), "=m" (*pAddr)
207 : "0" ((long) (1)), "m" (*pAddr));
210 #else // Linux / OSX86 (GCC)
211 register long reg __asm__ ("eax") = 1;
212 __asm__ __volatile__ (
213 "lock/xadd %0, %1 \n"
223 ///////////////////////////////////////////////////////////////////////////
225 // Returns new value of *pAddr
226 ///////////////////////////////////////////////////////////////////////////
227 long AtomicAdd(volatile long* pAddr, long amount)
229 #if defined(HAS_BUILTIN_SYNC_ADD_AND_FETCH)
230 return __sync_add_and_fetch(pAddr, amount);
232 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
234 __asm__ __volatile__ (
236 "1: lwarx %0, 0, %1 \n"
238 "stwcx. %0, 0, %1 \n"
242 : "r" (pAddr), "r" (amount)
246 #elif defined(__arm__) && !defined(__ARM_ARCH_5__)
249 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
251 "ldrex %0, [%1] \n" // (val = *pAddr)
252 "add %0, %2 \n" // (val += amount)
253 "strex r1, %0, [%1] \n"
256 "dmb ish \n" // Memory barrier.
258 : "r"(pAddr), "r"(amount)
263 #elif defined(__mips__)
266 #error AtomicAdd undefined for mips
269 #elif defined(TARGET_WINDOWS)
274 lock xadd dword ptr [ebx], eax;
280 #elif defined(__x86_64__)
281 register long result;
282 __asm__ __volatile__ (
284 : "=r" (result), "=m" (*pAddr)
285 : "0" ((long) (amount)), "m" (*pAddr));
288 #else // Linux / OSX86 (GCC)
289 register long reg __asm__ ("eax") = amount;
290 __asm__ __volatile__ (
291 "lock/xadd %0, %1 \n"
301 ///////////////////////////////////////////////////////////////////////////
302 // 32-bit atomic decrement
303 // Returns new value of *pAddr
304 ///////////////////////////////////////////////////////////////////////////
305 long AtomicDecrement(volatile long* pAddr)
307 #if defined(HAS_BUILTIN_SYNC_SUB_AND_FETCH)
308 return __sync_sub_and_fetch(pAddr, 1);
310 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
312 __asm__ __volatile__ (
314 "1: lwarx %0, 0, %1 \n"
315 "addic %0, %0, -1 \n"
316 "stwcx. %0, 0, %1 \n"
321 : "cc", "xer", "memory");
324 #elif defined(__arm__)
327 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
329 "ldrex %0, [%1] \n" // (val = *pAddr)
330 "sub %0, #1 \n" // (val -= 1)
331 "strex r1, %0, [%1] \n"
334 "dmb ish \n" // Memory barrier.
341 #elif defined(__mips__)
344 #error AtomicDecrement undefined for mips
347 #elif defined(TARGET_WINDOWS)
352 lock dec dword ptr [eax] ;
358 #elif defined(__x86_64__)
359 register long result;
360 __asm__ __volatile__ (
362 : "=r" (result), "=m" (*pAddr)
363 : "0" ((long) (-1)), "m" (*pAddr));
366 #else // Linux / OSX86 (GCC)
367 register long reg __asm__ ("eax") = -1;
368 __asm__ __volatile__ (
369 "lock/xadd %0, %1 \n"
379 ///////////////////////////////////////////////////////////////////////////
380 // 32-bit atomic subtract
381 // Returns new value of *pAddr
382 ///////////////////////////////////////////////////////////////////////////
383 long AtomicSubtract(volatile long* pAddr, long amount)
385 #if defined(HAS_BUILTIN_SYNC_SUB_AND_FETCH)
386 return __sync_sub_and_fetch(pAddr, amount);
388 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
391 __asm__ __volatile__ (
393 "1: lwarx %0, 0, %1 \n"
395 "stwcx. %0, 0, %1 \n"
399 : "r" (pAddr), "r" (amount)
403 #elif defined(__arm__)
406 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
408 "ldrex %0, [%1] \n" // (val = *pAddr)
409 "sub %0, %2 \n" // (val -= amount)
410 "strex r1, %0, [%1] \n"
413 "dmb ish \n" // Memory barrier.
415 : "r"(pAddr), "r"(amount)
420 #elif defined(__mips__)
422 #error AtomicSubtract undefined for mips
425 #elif defined(TARGET_WINDOWS)
431 lock xadd dword ptr [ebx], eax;
437 #elif defined(__x86_64__)
438 register long result;
439 __asm__ __volatile__ (
441 : "=r" (result), "=m" (*pAddr)
442 : "0" ((long) (-1 * amount)), "m" (*pAddr));
445 #else // Linux / OSX86 (GCC)
446 register long reg __asm__ ("eax") = -1 * amount;
447 __asm__ __volatile__ (
448 "lock/xadd %0, %1 \n"
458 ///////////////////////////////////////////////////////////////////////////
459 // Fast spinlock implmentation. No backoff when busy
460 ///////////////////////////////////////////////////////////////////////////
461 CAtomicSpinLock::CAtomicSpinLock(long& lock) : m_Lock(lock)
463 while (cas(&m_Lock, 0, 1) != 0) {} // Lock
465 CAtomicSpinLock::~CAtomicSpinLock()
467 m_Lock = 0; // Unlock