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