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