fix address line in GPL headers
[vuplus_xbmc] / xbmc / threads / Atomics.cpp
1 /*
2  *      Copyright (C) 2005-2009 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  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 General Public License for more details.
14  *
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/>.
18  *
19  */
20
21 #include "Atomics.h"
22
23 // the only safe way to be absolutly sure that
24 // gcc intrinsics are present when using an unknown GCC
25 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
26   #define HAS_GCC_INTRINSICS
27 #elif defined(TARGET_DARWIN)
28   // safe under darwin gcc-4.2, llvm-gcc-4.2 and clang
29   #define HAS_GCC_INTRINSICS
30 #elif defined(TARGET_FREEBSD)
31   // safe under freebsd gcc-4.2 and clang
32   #define HAS_GCC_INTRINSICS
33 #endif
34 ///////////////////////////////////////////////////////////////////////////
35 // 32-bit atomic compare-and-swap
36 // Returns previous value of *pAddr
37 ///////////////////////////////////////////////////////////////////////////
38 long cas(volatile long *pAddr, long expectedVal, long swapVal)
39 {
40 #if defined(HAS_GCC_INTRINSICS)
41   return(__sync_val_compare_and_swap(pAddr, expectedVal, swapVal));
42 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
43   unsigned int prev;
44   __asm__ __volatile__ (
45     "  1:      lwarx   %0,0,%2  \n" /* Load the current value of *pAddr(%2) into prev (%0) and lock pAddr,  */
46     "          cmpw     0,%0,%3 \n" /* Verify that the current value (%2) == old value (%3) */
47     "          bne-     2f      \n" /* Bail if the two values are not equal [not as expected] */
48     "          stwcx.  %4,0,%2  \n" /* Attempt to store swapVal (%4) value into *pAddr (%2) [p must still be reserved] */
49     "          bne-    1b       \n" /* Loop if p was no longer reserved */
50     "          isync            \n" /* Reconcile multiple processors [if present] */
51     "  2:                       \n"
52     : "=&r" (prev), "+m" (*pAddr)                   /* Outputs [prev, *pAddr] */
53     : "r" (pAddr), "r" (expectedVal), "r" (swapVal) /* Inputs [pAddr, expectedVal, swapVal] */
54     : "cc", "memory");                              /* Clobbers */
55   return prev;
56
57 #elif defined(__arm__)
58   register long prev;
59   asm volatile (
60     "dmb      ish            \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
61     "1:                      \n"
62     "ldrex    %0, [%1]       \n" // Load the current value of *pAddr(%1) into prev (%0) and lock pAddr,
63     "cmp      %0,  %2        \n" // Verify that the current value (%0) == old value (%2)
64     "bne      2f             \n" // Bail if the two values are not equal [not as expected]
65     "strex    r1,  %3, [%1]  \n"
66     "cmp      r1,  #0        \n"
67     "bne      1b             \n"
68     "dmb      ish            \n" // Memory barrier.
69     "2:                      \n"
70     : "=&r" (prev)
71     : "r"(pAddr), "r"(expectedVal),"r"(swapVal)
72     : "r1"
73     );
74   return prev;
75
76 #elif defined(__mips__)
77 // TODO:
78   unsigned int prev;
79   #error atomic cas undefined for mips
80   return prev;
81
82 #elif defined(WIN32)
83   long prev;
84   __asm
85   {
86     // Load parameters
87     mov eax, expectedVal ;
88     mov ebx, pAddr ;
89     mov ecx, swapVal ;
90
91     // Do Swap
92     lock cmpxchg dword ptr [ebx], ecx ;
93
94     // Store the return value
95     mov prev, eax;
96   }
97   return prev;
98
99 #else // Linux / OSX86 (GCC)
100   long prev;
101   __asm__ __volatile__ (
102     "lock/cmpxchg %1, %2"
103     : "=a" (prev)
104     : "r" (swapVal), "m" (*pAddr), "0" (expectedVal)
105     : "memory" );
106   return prev;
107
108 #endif
109 }
110
111 ///////////////////////////////////////////////////////////////////////////
112 // 64-bit atomic compare-and-swap
113 // Returns previous value of *pAddr
114 ///////////////////////////////////////////////////////////////////////////
115 long long cas2(volatile long long* pAddr, long long expectedVal, long long swapVal)
116 {
117 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || defined(__mips__) // PowerPC, ARM, and MIPS
118 // Not available/required
119 // Hack to allow compilation
120   throw "cas2 is not implemented";
121
122 #elif defined(WIN32)
123   long long prev;
124   __asm
125   {
126     mov esi, pAddr ;
127     mov eax, dword ptr [expectedVal] ;
128     mov edx, dword ptr expectedVal[4] ;
129     mov ebx, dword ptr [swapVal] ;
130     mov ecx, dword ptr swapVal[4] ;
131     lock cmpxchg8b qword ptr [esi] ;
132     mov dword ptr [prev], eax ;
133     mov dword ptr prev[4], edx ;
134   }
135   return prev;
136
137 #else // Linux / OSX86 (GCC)
138   #if !defined (__x86_64)
139       long long prev;
140       __asm__ volatile (
141         " push %%ebx        \n"  // We have to manually handle ebx, because PIC uses it and the compiler refuses to build anything that touches it
142         " mov %4, %%ebx     \n"
143         " lock/cmpxchg8b (%%esi) \n"
144         " pop %%ebx"
145         : "=A" (prev)
146         : "c" ((unsigned long)(swapVal >> 32)), "0" (expectedVal), "S" (pAddr), "m" (swapVal)
147         : "memory");
148       return prev;
149   #else
150     // Hack to allow compilation on x86_64
151       throw "cas2 is not implemented on x86_64!";
152   #endif
153 #endif
154 }
155
156 ///////////////////////////////////////////////////////////////////////////
157 // 32-bit atomic increment
158 // Returns new value of *pAddr
159 ///////////////////////////////////////////////////////////////////////////
160 long AtomicIncrement(volatile long* pAddr)
161 {
162 #if defined(HAS_GCC_INTRINSICS)
163   return __sync_add_and_fetch(pAddr, 1);
164
165 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
166   long val;
167   __asm__ __volatile__ (
168     "sync             \n"
169     "1: lwarx  %0, 0, %1 \n"
170     "addic  %0, %0, 1 \n"
171     "stwcx. %0, 0, %1 \n"
172     "bne-   1b        \n"
173     "isync"
174     : "=&r" (val)
175     : "r" (pAddr)
176     : "cc", "xer", "memory");
177   return val;
178
179 #elif defined(__arm__) && !defined(__ARM_ARCH_5__)
180   register long val;
181   asm volatile (
182     "dmb      ish            \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
183     "1:                     \n" 
184     "ldrex   %0, [%1]       \n" // (val = *pAddr)
185     "add     %0,  #1        \n" // (val += 1)
186     "strex   r1,  %0, [%1]      \n"
187     "cmp     r1,   #0       \n"
188     "bne     1b             \n"
189     "dmb     ish            \n" // Memory barrier.
190     : "=&r" (val)
191     : "r"(pAddr)
192     : "r1"
193     );
194   return val;
195
196 #elif defined(__mips__)
197 // TODO:
198   long val;
199   #error AtomicIncrement undefined for mips
200   return val;
201
202 #elif defined(WIN32)
203   long val;
204   __asm
205   {
206     mov eax, pAddr ;
207     lock inc dword ptr [eax] ;
208     mov eax, [eax] ;
209     mov val, eax ;
210   }
211   return val;
212
213 #elif defined(__x86_64__)
214   register long result;
215   __asm__ __volatile__ (
216     "lock/xaddq %q0, %1"
217     : "=r" (result), "=m" (*pAddr)
218     : "0" ((long) (1)), "m" (*pAddr));
219   return *pAddr;
220
221 #else // Linux / OSX86 (GCC)
222   register long reg __asm__ ("eax") = 1;
223   __asm__ __volatile__ (
224     "lock/xadd %0, %1 \n"
225     "inc %%eax"
226     : "+r" (reg)
227     : "m" (*pAddr)
228     : "memory" );
229   return reg;
230
231 #endif
232 }
233
234 ///////////////////////////////////////////////////////////////////////////
235 // 32-bit atomic add
236 // Returns new value of *pAddr
237 ///////////////////////////////////////////////////////////////////////////
238 long AtomicAdd(volatile long* pAddr, long amount)
239 {
240 #if defined(HAS_GCC_INTRINSICS)
241   return __sync_add_and_fetch(pAddr, amount);
242
243 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
244   long val;
245   __asm__ __volatile__ (
246     "sync                 \n"
247     "1: lwarx  %0, 0, %1  \n"
248     "add  %0, %2, %0      \n"
249     "stwcx. %0, 0, %1     \n"
250     "bne-   1b            \n"
251     "isync"
252     : "=&r" (val)
253     : "r" (pAddr), "r" (amount)
254     : "cc", "memory");
255   return val;
256
257 #elif defined(__arm__) && !defined(__ARM_ARCH_5__)
258   register long val;
259   asm volatile (
260     "dmb      ish           \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
261   "1:                       \n" 
262     "ldrex   %0, [%1]       \n" // (val = *pAddr)
263     "add     %0,  %2        \n" // (val += amount)
264     "strex   r1,  %0, [%1]      \n"
265     "cmp     r1,   #0       \n"
266     "bne     1b             \n"
267     "dmb     ish            \n" // Memory barrier.
268     : "=&r" (val)
269     : "r"(pAddr), "r"(amount)
270     : "r1"
271     );
272   return val;
273
274 #elif defined(__mips__)
275 // TODO:
276   long val;
277   #error AtomicAdd undefined for mips
278   return val;
279
280 #elif defined(WIN32)
281   __asm
282   {
283     mov eax, amount;
284     mov ebx, pAddr;
285     lock xadd dword ptr [ebx], eax;
286     mov ebx, [ebx];
287     mov amount, ebx;
288   }
289   return amount;
290
291 #elif defined(__x86_64__)
292   register long result;
293   __asm__ __volatile__ (
294     "lock/xaddq %q0, %1"
295     : "=r" (result), "=m" (*pAddr)
296     : "0" ((long) (amount)), "m" (*pAddr));
297   return *pAddr;
298
299 #else // Linux / OSX86 (GCC)
300   register long reg __asm__ ("eax") = amount;
301   __asm__ __volatile__ (
302     "lock/xadd %0, %1 \n"
303     "dec %%eax"
304     : "+r" (reg)
305     : "m" (*pAddr)
306     : "memory" );
307   return reg;
308
309 #endif
310 }
311
312 ///////////////////////////////////////////////////////////////////////////
313 // 32-bit atomic decrement
314 // Returns new value of *pAddr
315 ///////////////////////////////////////////////////////////////////////////
316 long AtomicDecrement(volatile long* pAddr)
317 {
318 #if defined(HAS_GCC_INTRINSICS)
319   return __sync_sub_and_fetch(pAddr, 1);
320
321 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
322   long val;
323   __asm__ __volatile__ (
324     "sync                \n"
325 "1: lwarx  %0, 0, %1     \n"
326     "addic  %0, %0, -1   \n"
327     "stwcx. %0, 0, %1    \n"
328     "bne-   1b           \n"
329     "isync"
330     : "=&r" (val)
331     : "r" (pAddr)
332     : "cc", "xer", "memory");
333   return val;
334
335 #elif defined(__arm__)
336   register long val;
337   asm volatile (
338     "dmb      ish           \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
339     "1:                     \n" 
340     "ldrex   %0, [%1]       \n" // (val = *pAddr)
341     "sub     %0,  #1        \n" // (val -= 1)
342     "strex   r1,  %0, [%1]      \n"
343     "cmp     r1,   #0       \n"
344     "bne     1b             \n"
345     "dmb     ish            \n" // Memory barrier.
346     : "=&r" (val)
347     : "r"(pAddr)
348     : "r1"
349     );
350   return val;
351
352 #elif defined(__mips__)
353 // TODO:
354   long val;
355   #error AtomicDecrement undefined for mips
356   return val;
357
358 #elif defined(WIN32)
359   long val;
360   __asm
361   {
362     mov eax, pAddr ;
363     lock dec dword ptr [eax] ;
364     mov eax, [eax] ;
365     mov val, eax ;
366   }
367   return val;
368
369 #elif defined(__x86_64__)
370   register long result;
371   __asm__ __volatile__ (
372     "lock/xaddq %q0, %1"
373     : "=r" (result), "=m" (*pAddr)
374     : "0" ((long) (-1)), "m" (*pAddr));
375   return *pAddr;
376
377 #else // Linux / OSX86 (GCC)
378   register long reg __asm__ ("eax") = -1;
379   __asm__ __volatile__ (
380     "lock/xadd %0, %1 \n"
381     "dec %%eax"
382     : "+r" (reg)
383     : "m" (*pAddr)
384     : "memory" );
385   return reg;
386
387 #endif
388 }
389
390 ///////////////////////////////////////////////////////////////////////////
391 // 32-bit atomic subtract
392 // Returns new value of *pAddr
393 ///////////////////////////////////////////////////////////////////////////
394 long AtomicSubtract(volatile long* pAddr, long amount)
395 {
396 #if defined(HAS_GCC_INTRINSICS)
397   return __sync_sub_and_fetch(pAddr, amount);
398
399 #elif defined(__ppc__) || defined(__powerpc__) // PowerPC
400   long val;
401   amount *= -1;
402   __asm__ __volatile__ (
403     "sync                 \n"
404     "1: lwarx  %0, 0, %1  \n"
405     "add  %0, %2, %0      \n"
406     "stwcx. %0, 0, %1     \n"
407     "bne-   1b            \n"
408     "isync"
409     : "=&r" (val)
410     : "r" (pAddr), "r" (amount)
411     : "cc", "memory");
412   return val;
413
414 #elif defined(__arm__)
415   register long val;
416   asm volatile (
417     "dmb     ish            \n" // Memory barrier. Make sure all memory accesses appearing before this complete before any that appear after
418     "1:                     \n" 
419     "ldrex   %0, [%1]       \n" // (val = *pAddr)
420     "sub     %0,  %2        \n" // (val -= amount)
421     "strex   r1,  %0, [%1]      \n"
422     "cmp     r1,   #0       \n"
423     "bne     1b             \n"
424     "dmb     ish            \n" // Memory barrier.
425     : "=&r" (val)
426     : "r"(pAddr), "r"(amount)
427     : "r1"
428     );
429   return val;
430
431 #elif defined(__mips__)
432 // TODO:
433   #error AtomicSubtract undefined for mips
434   return val;
435
436 #elif defined(WIN32)
437   amount *= -1;
438   __asm
439   {
440     mov eax, amount;
441     mov ebx, pAddr;
442     lock xadd dword ptr [ebx], eax;
443     mov ebx, [ebx];
444     mov amount, ebx;
445   }
446   return amount;
447
448 #elif defined(__x86_64__)
449   register long result;
450   __asm__ __volatile__ (
451     "lock/xaddq %q0, %1"
452     : "=r" (result), "=m" (*pAddr)
453     : "0" ((long) (-1 * amount)), "m" (*pAddr));
454   return *pAddr;
455
456 #else // Linux / OSX86 (GCC)
457   register long reg __asm__ ("eax") = -1 * amount;
458   __asm__ __volatile__ (
459     "lock/xadd %0, %1 \n"
460     "dec %%eax"
461     : "+r" (reg)
462     : "m" (*pAddr)
463     : "memory" );
464   return reg;
465
466 #endif
467 }
468
469 ///////////////////////////////////////////////////////////////////////////
470 // Fast spinlock implmentation. No backoff when busy
471 ///////////////////////////////////////////////////////////////////////////
472 CAtomicSpinLock::CAtomicSpinLock(long& lock) : m_Lock(lock)
473 {
474   while (cas(&m_Lock, 0, 1) != 0) {} // Lock
475 }
476 CAtomicSpinLock::~CAtomicSpinLock()
477 {
478   m_Lock = 0; // Unlock
479 }