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/>.
24 // oskwon :: cas/cas2, not implemented yet.
26 ///////////////////////////////////////////////////////////////////////////
27 // 32-bit atomic compare-and-swap
28 // Returns previous value of *pAddr
29 ///////////////////////////////////////////////////////////////////////////
30 long cas(volatile long *pAddr, long expectedVal, long swapVal)
32 #if defined(HAS_BUILTIN_SYNC_VAL_COMPARE_AND_SWAP)
33 return(__sync_val_compare_and_swap(pAddr, expectedVal, swapVal));
34 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
36 __asm__ __volatile__ (
37 " 1: lwarx %0,0,%2 \n" /* Load the current value of *pAddr(%2) into prev (%0) and lock pAddr, */
38 " cmpw 0,%0,%3 \n" /* Verify that the current value (%2) == old value (%3) */
39 " bne- 2f \n" /* Bail if the two values are not equal [not as expected] */
40 " stwcx. %4,0,%2 \n" /* Attempt to store swapVal (%4) value into *pAddr (%2) [p must still be reserved] */
41 " bne- 1b \n" /* Loop if p was no longer reserved */
42 " isync \n" /* Reconcile multiple processors [if present] */
44 : "=&r" (prev), "+m" (*pAddr) /* Outputs [prev, *pAddr] */
45 : "r" (pAddr), "r" (expectedVal), "r" (swapVal) /* Inputs [pAddr, expectedVal, swapVal] */
46 : "cc", "memory"); /* Clobbers */
49 #elif defined(__arm__)
52 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
54 "ldrex %0, [%1] \n" // Load the current value of *pAddr(%1) into prev (%0) and lock pAddr,
55 "cmp %0, %2 \n" // Verify that the current value (%0) == old value (%2)
56 "bne 2f \n" // Bail if the two values are not equal [not as expected]
57 "strex r1, %3, [%1] \n"
60 "dmb ish \n" // Memory barrier.
63 : "r"(pAddr), "r"(expectedVal),"r"(swapVal)
68 #elif defined(__mips__)
71 #error atomic cas undefined for mips
74 #elif defined(TARGET_WINDOWS)
79 mov eax, expectedVal ;
84 lock cmpxchg dword ptr [ebx], ecx ;
86 // Store the return value
91 #else // Linux / OSX86 (GCC)
93 __asm__ __volatile__ (
96 : "r" (swapVal), "m" (*pAddr), "0" (expectedVal)
103 ///////////////////////////////////////////////////////////////////////////
104 // 64-bit atomic compare-and-swap
105 // Returns previous value of *pAddr
106 ///////////////////////////////////////////////////////////////////////////
107 long long cas2(volatile long long* pAddr, long long expectedVal, long long swapVal)
109 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || defined(__mips__) // PowerPC, ARM, and MIPS
110 // Not available/required
111 // Hack to allow compilation
112 throw "cas2 is not implemented";
114 #elif defined(TARGET_WINDOWS)
119 mov eax, dword ptr [expectedVal] ;
120 mov edx, dword ptr expectedVal[4] ;
121 mov ebx, dword ptr [swapVal] ;
122 mov ecx, dword ptr swapVal[4] ;
123 lock cmpxchg8b qword ptr [esi] ;
124 mov dword ptr [prev], eax ;
125 mov dword ptr prev[4], edx ;
129 #else // Linux / OSX86 (GCC)
130 #if !defined (__x86_64)
133 " push %%ebx \n" // We have to manually handle ebx, because PIC uses it and the compiler refuses to build anything that touches it
135 " lock/cmpxchg8b (%%esi) \n"
138 : "c" ((unsigned long)(swapVal >> 32)), "0" (expectedVal), "S" (pAddr), "m" (swapVal)
142 // Hack to allow compilation on x86_64
143 throw "cas2 is not implemented on x86_64!";
148 ///////////////////////////////////////////////////////////////////////////
149 // 32-bit atomic increment
150 // Returns new value of *pAddr
151 ///////////////////////////////////////////////////////////////////////////
152 long AtomicIncrement(volatile long* pAddr)
154 #if defined(HAS_BUILTIN_SYNC_ADD_AND_FETCH)
155 return __sync_add_and_fetch(pAddr, 1);
157 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
159 __asm__ __volatile__ (
161 "1: lwarx %0, 0, %1 \n"
163 "stwcx. %0, 0, %1 \n"
168 : "cc", "xer", "memory");
171 #elif defined(__arm__) && !defined(__ARM_ARCH_5__)
174 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
176 "ldrex %0, [%1] \n" // (val = *pAddr)
177 "add %0, #1 \n" // (val += 1)
178 "strex r1, %0, [%1] \n"
181 "dmb ish \n" // Memory barrier.
188 #elif defined(__mips__)
191 #error AtomicIncrement undefined for mips
194 #elif defined(TARGET_WINDOWS)
199 lock inc dword ptr [eax] ;
205 #elif defined(__x86_64__)
206 register long result;
207 __asm__ __volatile__ (
209 : "=r" (result), "=m" (*pAddr)
210 : "0" ((long) (1)), "m" (*pAddr));
213 #else // Linux / OSX86 (GCC)
214 register long reg __asm__ ("eax") = 1;
215 __asm__ __volatile__ (
216 "lock/xadd %0, %1 \n"
226 ///////////////////////////////////////////////////////////////////////////
228 // Returns new value of *pAddr
229 ///////////////////////////////////////////////////////////////////////////
230 long AtomicAdd(volatile long* pAddr, long amount)
232 #if defined(HAS_BUILTIN_SYNC_ADD_AND_FETCH)
233 return __sync_add_and_fetch(pAddr, amount);
235 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
237 __asm__ __volatile__ (
239 "1: lwarx %0, 0, %1 \n"
241 "stwcx. %0, 0, %1 \n"
245 : "r" (pAddr), "r" (amount)
249 #elif defined(__arm__) && !defined(__ARM_ARCH_5__)
252 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
254 "ldrex %0, [%1] \n" // (val = *pAddr)
255 "add %0, %2 \n" // (val += amount)
256 "strex r1, %0, [%1] \n"
259 "dmb ish \n" // Memory barrier.
261 : "r"(pAddr), "r"(amount)
266 #elif defined(__mips__)
269 #error AtomicAdd undefined for mips
272 #elif defined(TARGET_WINDOWS)
277 lock xadd dword ptr [ebx], eax;
283 #elif defined(__x86_64__)
284 register long result;
285 __asm__ __volatile__ (
287 : "=r" (result), "=m" (*pAddr)
288 : "0" ((long) (amount)), "m" (*pAddr));
291 #else // Linux / OSX86 (GCC)
292 register long reg __asm__ ("eax") = amount;
293 __asm__ __volatile__ (
294 "lock/xadd %0, %1 \n"
304 ///////////////////////////////////////////////////////////////////////////
305 // 32-bit atomic decrement
306 // Returns new value of *pAddr
307 ///////////////////////////////////////////////////////////////////////////
308 long AtomicDecrement(volatile long* pAddr)
310 #if defined(HAS_BUILTIN_SYNC_SUB_AND_FETCH)
311 return __sync_sub_and_fetch(pAddr, 1);
313 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
315 __asm__ __volatile__ (
317 "1: lwarx %0, 0, %1 \n"
318 "addic %0, %0, -1 \n"
319 "stwcx. %0, 0, %1 \n"
324 : "cc", "xer", "memory");
327 #elif defined(__arm__)
330 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
332 "ldrex %0, [%1] \n" // (val = *pAddr)
333 "sub %0, #1 \n" // (val -= 1)
334 "strex r1, %0, [%1] \n"
337 "dmb ish \n" // Memory barrier.
344 #elif defined(__mips__)
347 #error AtomicDecrement undefined for mips
350 #elif defined(TARGET_WINDOWS)
355 lock dec dword ptr [eax] ;
361 #elif defined(__x86_64__)
362 register long result;
363 __asm__ __volatile__ (
365 : "=r" (result), "=m" (*pAddr)
366 : "0" ((long) (-1)), "m" (*pAddr));
369 #else // Linux / OSX86 (GCC)
370 register long reg __asm__ ("eax") = -1;
371 __asm__ __volatile__ (
372 "lock/xadd %0, %1 \n"
382 ///////////////////////////////////////////////////////////////////////////
383 // 32-bit atomic subtract
384 // Returns new value of *pAddr
385 ///////////////////////////////////////////////////////////////////////////
386 long AtomicSubtract(volatile long* pAddr, long amount)
388 #if defined(HAS_BUILTIN_SYNC_SUB_AND_FETCH)
389 return __sync_sub_and_fetch(pAddr, amount);
391 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
394 __asm__ __volatile__ (
396 "1: lwarx %0, 0, %1 \n"
398 "stwcx. %0, 0, %1 \n"
402 : "r" (pAddr), "r" (amount)
406 #elif defined(__arm__)
409 "dmb ish \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
411 "ldrex %0, [%1] \n" // (val = *pAddr)
412 "sub %0, %2 \n" // (val -= amount)
413 "strex r1, %0, [%1] \n"
416 "dmb ish \n" // Memory barrier.
418 : "r"(pAddr), "r"(amount)
423 #elif defined(__mips__)
425 #error AtomicSubtract undefined for mips
428 #elif defined(TARGET_WINDOWS)
434 lock xadd dword ptr [ebx], eax;
440 #elif defined(__x86_64__)
441 register long result;
442 __asm__ __volatile__ (
444 : "=r" (result), "=m" (*pAddr)
445 : "0" ((long) (-1 * amount)), "m" (*pAddr));
448 #else // Linux / OSX86 (GCC)
449 register long reg __asm__ ("eax") = -1 * amount;
450 __asm__ __volatile__ (
451 "lock/xadd %0, %1 \n"
461 ///////////////////////////////////////////////////////////////////////////
462 // Fast spinlock implmentation. No backoff when busy
463 ///////////////////////////////////////////////////////////////////////////
464 CAtomicSpinLock::CAtomicSpinLock(long& lock) : m_Lock(lock)
466 while (cas(&m_Lock, 0, 1) != 0) {} // Lock
468 CAtomicSpinLock::~CAtomicSpinLock()
470 m_Lock = 0; // Unlock