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