gst-indent run on core
[platform/upstream/gstreamer.git] / gst / gstatomic_impl.h
1 /* GStreamer
2  * Copyright (C) 1999, 2003 Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  *
20  * Much of the code in this file is taken from the Linux kernel. 
21  * The code is relicensed under the LGPL with the kind permission of
22  * Linus Torvalds,Ralf Baechle and Alan Cox
23  */
24
25 #ifndef __GST_ATOMIC_IMPL_H__
26 #define __GST_ATOMIC_IMPL_H__
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <glib.h>
33 #include "gstatomic.h"
34 #include "gstmacros.h"
35
36 G_BEGIN_DECLS
37 #if defined (GST_CAN_INLINE) || defined (__GST_ATOMIC_C__)
38 /***** Intel x86 *****/
39 #if defined (HAVE_CPU_I386) && defined(__GNUC__)
40 #ifdef GST_CONFIG_NO_SMP
41 #define SMP_LOCK ""
42 #else
43 #define SMP_LOCK "lock ; "
44 #endif
45     GST_INLINE_FUNC void
46 gst_atomic_int_init (GstAtomicInt * aint, gint val)
47 {
48   aint->counter = val;
49 }
50 GST_INLINE_FUNC void
51 gst_atomic_int_destroy (GstAtomicInt * aint)
52 {
53 }
54 GST_INLINE_FUNC void
55 gst_atomic_int_set (GstAtomicInt * aint, gint val)
56 {
57   aint->counter = val;
58 }
59 GST_INLINE_FUNC gint
60 gst_atomic_int_read (GstAtomicInt * aint)
61 {
62   return aint->counter;
63 }
64
65 GST_INLINE_FUNC void
66 gst_atomic_int_add (GstAtomicInt * aint, gint val)
67 {
68   __asm__ __volatile__ (SMP_LOCK "addl %1,%0":"=m" (aint->counter)
69       :"ir" (val), "m" (aint->counter));
70 }
71
72 GST_INLINE_FUNC void
73 gst_atomic_int_inc (GstAtomicInt * aint)
74 {
75   __asm__ __volatile__ (SMP_LOCK "incl %0":"=m" (aint->counter)
76       :"m" (aint->counter));
77 }
78
79 GST_INLINE_FUNC gboolean
80 gst_atomic_int_dec_and_test (GstAtomicInt * aint)
81 {
82   guchar res;
83
84   __asm__ __volatile__ (SMP_LOCK "decl %0; sete %1":"=m" (aint->counter),
85       "=qm" (res)
86       :"m" (aint->counter):"memory");
87
88   return res != 0;
89 }
90
91 /***** PowerPC *****/
92 #elif defined (HAVE_CPU_PPC) && defined(__GNUC__)
93 #ifdef GST_CONFIG_NO_SMP
94 #define SMP_SYNC        ""
95 #define SMP_ISYNC
96 #else
97 #define SMP_SYNC        "\tsync\n"
98 #define SMP_ISYNC       "\tisync\n"
99 #endif
100 /* Erratum #77 on the 405 means we need a sync or dcbt before every stwcx.
101  * The old ATOMIC_SYNC_FIX covered some but not all of this.
102  */
103 #ifdef GST_CONFIG_IBM405_ERR77
104 #define PPC405_ERR77(ra,rb)     "\tdcbt " #ra "," #rb "\n"
105 #else
106 #define PPC405_ERR77(ra,rb)
107 #endif
108     GST_INLINE_FUNC void
109 gst_atomic_int_init (GstAtomicInt * aint, gint val)
110 {
111   aint->counter = val;
112 }
113 GST_INLINE_FUNC void
114 gst_atomic_int_destroy (GstAtomicInt * aint)
115 {
116 }
117 GST_INLINE_FUNC void
118 gst_atomic_int_set (GstAtomicInt * aint, gint val)
119 {
120   aint->counter = val;
121 }
122 GST_INLINE_FUNC gint
123 gst_atomic_int_read (GstAtomicInt * aint)
124 {
125   return aint->counter;
126 }
127
128 GST_INLINE_FUNC void
129 gst_atomic_int_add (GstAtomicInt * aint, gint val)
130 {
131   int t;
132
133   __asm__ __volatile__ ("1:     lwarx   %0,0,%3\n"
134       "       add     %0,%2,%0\n" PPC405_ERR77 (0, %3)
135       "       stwcx.  %0,0,%3 \n"
136       "       bne-    1b\n":"=&r" (t), "=m" (aint->counter)
137       :"r" (val), "r" (&aint->counter), "m" (aint->counter)
138       :"cc");
139 }
140
141 GST_INLINE_FUNC void
142 gst_atomic_int_inc (GstAtomicInt * aint)
143 {
144   int t;
145
146   __asm__ __volatile__ ("1:     lwarx   %0,0,%2\n"
147       "       addic   %0,%0,1\n" PPC405_ERR77 (0, %2)
148       "       stwcx.  %0,0,%2\n"
149       "       bne-    1b\n":"=&r" (t), "=m" (aint->counter)
150       :"r" (&aint->counter), "m" (aint->counter)
151       :"cc");
152 }
153
154 GST_INLINE_FUNC gboolean
155 gst_atomic_int_dec_and_test (GstAtomicInt * aint)
156 {
157   int t;
158
159   __asm__ __volatile__ ("1:     lwarx   %0,0,%1\n"
160       "       addic   %0,%0,-1\n" PPC405_ERR77 (0, %1)
161       "       stwcx.  %0,0,%1\n" "       bne-    1b\n" SMP_ISYNC:"=&r" (t)
162       :"r" (&aint->counter)
163       :"cc", "memory");
164
165   return t == 0;
166 }
167
168 /***** DEC[/Compaq/HP?/Intel?] Alpha *****/
169 #elif defined(HAVE_CPU_ALPHA) && defined(__GNUC__)
170     GST_INLINE_FUNC void
171 gst_atomic_int_init (GstAtomicInt * aint, gint val)
172 {
173   aint->counter = val;
174 }
175 GST_INLINE_FUNC void
176 gst_atomic_int_destroy (GstAtomicInt * aint)
177 {
178 }
179 GST_INLINE_FUNC void
180 gst_atomic_int_set (GstAtomicInt * aint, gint val)
181 {
182   aint->counter = val;
183 }
184 GST_INLINE_FUNC gint
185 gst_atomic_int_read (GstAtomicInt * aint)
186 {
187   return aint->counter;
188 }
189
190 GST_INLINE_FUNC void
191 gst_atomic_int_add (GstAtomicInt * aint, gint val)
192 {
193   unsigned long temp;
194
195   __asm__ __volatile__ ("1:     ldl_l %0,%1\n"
196       "       addl %0,%2,%0\n"
197       "       stl_c %0,%1\n"
198       "       beq %0,2f\n"
199       ".subsection 2\n"
200       "2:     br 1b\n" ".previous":"=&r" (temp), "=m" (aint->counter)
201       :"Ir" (val), "m" (aint->counter));
202 }
203
204 GST_INLINE_FUNC void
205 gst_atomic_int_inc (GstAtomicInt * aint)
206 {
207   gst_atomic_int_add (aint, 1);
208 }
209
210 GST_INLINE_FUNC gboolean
211 gst_atomic_int_dec_and_test (GstAtomicInt * aint)
212 {
213   long temp, result;
214   int val = 1;
215   __asm__ __volatile__ ("1:     ldl_l %0,%1\n"
216       "       subl %0,%3,%2\n"
217       "       subl %0,%3,%0\n"
218       "       stl_c %0,%1\n"
219       "       beq %0,2f\n"
220       "       mb\n"
221       ".subsection 2\n"
222       "2:     br 1b\n"
223       ".previous":"=&r" (temp), "=m" (aint->counter), "=&r" (result)
224       :"Ir" (val), "m" (aint->counter):"memory");
225
226   return result == 0;
227 }
228
229 /***** Sun SPARC *****/
230 #elif 0 && defined(HAVE_CPU_SPARC) && defined(__GNUC__)
231 /* allegedly broken again */
232     GST_INLINE_FUNC void
233 gst_atomic_int_destroy (GstAtomicInt * aint)
234 {
235 }
236
237 #ifdef GST_CONFIG_NO_SMP
238 GST_INLINE_FUNC void
239 gst_atomic_int_init (GstAtomicInt * aint, gint val)
240 {
241   aint->counter = val;
242 }
243 GST_INLINE_FUNC void
244 gst_atomic_int_set (GstAtomicInt * aint, gint val)
245 {
246   aint->counter = val;
247 }
248 GST_INLINE_FUNC gint
249 gst_atomic_int_read (GstAtomicInt * aint)
250 {
251   return aint->counter;
252 }
253 #else
254 GST_INLINE_FUNC void
255 gst_atomic_int_init (GstAtomicInt * aint, gint val)
256 {
257   aint->counter = (val << 8);
258 }
259 GST_INLINE_FUNC void
260 gst_atomic_int_set (GstAtomicInt * aint, gint val)
261 {
262   aint->counter = (val << 8);
263 }
264
265 /*
266  * For SMP the trick is you embed the spin lock byte within
267  * the word, use the low byte so signedness is easily retained
268  * via a quick arithmetic shift.  It looks like this:
269  *
270  *      ----------------------------------------
271  *      | signed 24-bit counter value |  lock  |  atomic_t
272  *      ----------------------------------------
273  *       31                          8 7      0
274  */
275 GST_INLINE_FUNC gint
276 gst_atomic_int_read (GstAtomicInt * aint)
277 {
278   int ret = aint->counter;
279
280   while (ret & 0xff)
281     ret = aint->counter;
282
283   return ret >> 8;
284 }
285 #endif /* GST_CONFIG_NO_SMP */
286
287 GST_INLINE_FUNC void
288 gst_atomic_int_add (GstAtomicInt * aint, gint val)
289 {
290   volatile int increment, *ptr;
291   int lock = 1;
292   int ignore = 0;
293
294   ptr = &(aint->counter);
295
296 #if __GNUC__ > 3 || (__GNUC__ >=3 && __GNUC_MINOR__ >= 2)
297   __asm__ __volatile__ ("1: ldstub [%[ptr] + 3], %[lock]\n" "\torcc %[lock], 0, %[ignore]\n" "\tbne 1b\n"       /* go back until we have the lock */
298       "\tld [%[ptr]], %[inc]\n" "\tsra %[inc], 8, %[inc]\n" "\tadd %[inc], %[val], %[inc]\n" "\tsll %[inc], 8, %[lock]\n" "\tst %[lock],[%[ptr]]\n"     /* Release the lock */
299       :[inc] "=&r" (increment),[lock] "=r" (lock),[ignore] "=&r" (ignore)
300       :"0" (increment),[ptr] "r" (ptr),[val] "r" (val)
301       );
302 #else
303   __asm__ __volatile__ ("1: ldstub [%4 + 3], %1\n" "\torcc %1, 0, %2\n" "\tbne 1b\n"    /* go back until we have the lock */
304       "\tld [%4], %0\n" "\tsra %0, 8, %0\n" "\tadd %0, %5, %0\n" "\tsll %0, 8, %1\n" "\tst %1,[%4]\n"   /* Release the lock */
305       :"=&r" (increment), "=r" (lock), "=&r" (ignore)
306       :"0" (increment), "r" (ptr), "r" (val)
307       );
308 #endif
309 }
310
311 GST_INLINE_FUNC void
312 gst_atomic_int_inc (GstAtomicInt * aint)
313 {
314   gst_atomic_int_add (aint, 1);
315 }
316
317 GST_INLINE_FUNC gboolean
318 gst_atomic_int_dec_and_test (GstAtomicInt * aint)
319 {
320   volatile int increment, *ptr;
321   int lock = 1;
322   int ignore = 0;
323
324   ptr = &aint->counter;
325
326 #if __GNUC__ > 3 || (__GNUC__ >=3 && __GNUC_MINOR__ >= 2)
327   __asm__ __volatile__ ("1: ldstub [%[ptr] + 3], %[lock]\n" "\torcc %[lock], 0, %[ignore]\n" "\tbne 1b\n"       /* go back until we have the lock */
328       "\tld [%[ptr]], %[inc]\n" "\tsra %[inc], 8, %[inc]\n" "\tsub %[inc], 1, %[inc]\n" "\tsll %[inc], 8, %[lock]\n" "\tst %[lock],[%[ptr]]\n"  /* Release the lock */
329       :[inc] "=&r" (increment),[lock] "=r" (lock),[ignore] "=&r" (ignore)
330       :"0" (increment),[ptr] "r" (ptr)
331       );
332 #else
333   __asm__ __volatile__ ("1: ldstub [%4 + 3], %1\n" "\torcc %1, 0, %2\n" "\tbne 1b\n"    /* go back until we have the lock */
334       "\tld [%4], %0\n" "\tsra %0, 8, %0\n" "\tsub %0, 1, %0\n" "\tsll %0, 8, %1\n" "\tst %1,[%4]\n"    /* Release the lock */
335       :"=&r" (increment), "=r" (lock), "=&r" (ignore)
336       :"0" (increment), "r" (ptr)
337       );
338 #endif
339
340   return increment == 0;
341 }
342
343 /***** MIPS *****/
344 /* This is disabled because the asm code is broken on most MIPS
345  * processors and doesn't generally compile. */
346 #elif defined(HAVE_CPU_MIPS) && defined(__GNUC__) && 0
347     GST_INLINE_FUNC void
348 gst_atomic_int_init (GstAtomicInt * aint, gint val)
349 {
350   aint->counter = val;
351 }
352 GST_INLINE_FUNC void
353 gst_atomic_int_destroy (GstAtomicInt * aint)
354 {
355 }
356 GST_INLINE_FUNC void
357 gst_atomic_int_set (GstAtomicInt * aint, gint val)
358 {
359   aint->counter = val;
360 }
361 GST_INLINE_FUNC gint
362 gst_atomic_int_read (GstAtomicInt * aint)
363 {
364   return aint->counter;
365 }
366
367 /* this only works on MIPS II and better */
368 GST_INLINE_FUNC void
369 gst_atomic_int_add (GstAtomicInt * aint, gint val)
370 {
371   unsigned long temp;
372
373   __asm__ __volatile__ ("1:   ll      %0, %1      # atomic_add\n"
374       "     addu    %0, %2                  \n"
375       "     sc      %0, %1                  \n"
376       "     beqz    %0, 1b                  \n":"=&r" (temp),
377       "=m" (aint->counter)
378       :"Ir" (val), "m" (aint->counter));
379 }
380
381 GST_INLINE_FUNC void
382 gst_atomic_int_inc (GstAtomicInt * aint)
383 {
384   gst_atomic_int_add (aint, 1);
385 }
386
387 GST_INLINE_FUNC gboolean
388 gst_atomic_int_dec_and_test (GstAtomicInt * aint)
389 {
390   unsigned long temp, result;
391   int val = 1;
392
393   __asm__ __volatile__ (".set push                                   \n"
394       ".set noreorder           # atomic_sub_return\n"
395       "1:   ll    %1, %2                           \n"
396       "     subu  %0, %1, %3                       \n"
397       "     sc    %0, %2                           \n"
398       "     beqz  %0, 1b                           \n"
399       "     subu  %0, %1, %3                       \n"
400       ".set pop                                    \n":"=&r" (result),
401       "=&r" (temp), "=m" (aint->counter)
402       :"Ir" (val), "m" (aint->counter)
403       :"memory");
404
405   return result == 0;
406 }
407
408 /***** S/390 *****/
409 #elif defined(HAVE_CPU_S390) && defined(__GNUC__)
410     typedef struct
411 {
412   volatile int counter;
413 } atomic_t __attribute__ ((aligned (4)));
414
415 GST_INLINE_FUNC void
416 gst_atomic_int_init (GstAtomicInt * aint, gint val)
417 {
418   aint->counter = val;
419 }
420 GST_INLINE_FUNC void
421 gst_atomic_int_destroy (GstAtomicInt * aint)
422 {
423 }
424 GST_INLINE_FUNC void
425 gst_atomic_int_set (GstAtomicInt * aint, gint val)
426 {
427   aint->counter = val;
428 }
429 GST_INLINE_FUNC gint
430 gst_atomic_int_read (GstAtomicInt * aint)
431 {
432   return aint->counter;
433 }
434
435 #define __CS_LOOP(old_val, new_val, ptr, op_val, op_string)             \
436         __asm__ __volatile__("   l     %0,0(%3)\n"                      \
437                              "0: lr    %1,%0\n"                         \
438                              op_string "  %1,%4\n"                      \
439                              "   cs    %0,%1,0(%3)\n"                   \
440                              "   jl    0b"                              \
441                              : "=&d" (old_val), "=&d" (new_val),        \
442                                "+m" (((atomic_t *)(ptr))->counter)      \
443                              : "a" (ptr), "d" (op_val) : "cc" );
444
445 GST_INLINE_FUNC void
446 gst_atomic_int_add (GstAtomicInt * aint, gint val)
447 {
448   int old_val, new_val;
449
450   __CS_LOOP (old_val, new_val, aint, val, "ar");
451 }
452
453 GST_INLINE_FUNC void
454 gst_atomic_int_inc (GstAtomicInt * aint)
455 {
456   int old_val, new_val;
457
458   __CS_LOOP (old_val, new_val, aint, 1, "ar");
459 }
460
461 GST_INLINE_FUNC gboolean
462 gst_atomic_int_dec_and_test (GstAtomicInt * aint)
463 {
464   int old_val, new_val;
465
466   __CS_LOOP (old_val, new_val, aint, 1, "sr");
467   return new_val == 0;
468 }
469
470 #else
471 #warning consider putting your architecture specific atomic implementations here
472 /*
473  * generic implementation
474  */
475     GST_INLINE_FUNC void
476 gst_atomic_int_init (GstAtomicInt * aint, gint val)
477 {
478   aint->counter = val;
479   aint->lock = g_mutex_new ();
480 }
481
482 GST_INLINE_FUNC void
483 gst_atomic_int_destroy (GstAtomicInt * aint)
484 {
485   g_mutex_free (aint->lock);
486 }
487
488 GST_INLINE_FUNC void
489 gst_atomic_int_set (GstAtomicInt * aint, gint val)
490 {
491   g_mutex_lock (aint->lock);
492   aint->counter = val;
493   g_mutex_unlock (aint->lock);
494 }
495
496 GST_INLINE_FUNC gint
497 gst_atomic_int_read (GstAtomicInt * aint)
498 {
499   gint res;
500
501   g_mutex_lock (aint->lock);
502   res = aint->counter;
503   g_mutex_unlock (aint->lock);
504
505   return res;
506 }
507
508 GST_INLINE_FUNC void
509 gst_atomic_int_add (GstAtomicInt * aint, gint val)
510 {
511   g_mutex_lock (aint->lock);
512   aint->counter += val;
513   g_mutex_unlock (aint->lock);
514 }
515
516 GST_INLINE_FUNC void
517 gst_atomic_int_inc (GstAtomicInt * aint)
518 {
519   g_mutex_lock (aint->lock);
520   aint->counter++;
521   g_mutex_unlock (aint->lock);
522 }
523
524 GST_INLINE_FUNC gboolean
525 gst_atomic_int_dec_and_test (GstAtomicInt * aint)
526 {
527   gboolean res;
528
529   g_mutex_lock (aint->lock);
530   aint->counter--;
531   res = (aint->counter == 0);
532   g_mutex_unlock (aint->lock);
533
534   return res;
535 }
536
537 #endif
538 /*
539  * common functions
540  */
541 GST_INLINE_FUNC GstAtomicInt *
542 gst_atomic_int_new (gint val)
543 {
544   GstAtomicInt *aint;
545
546   aint = g_new0 (GstAtomicInt, 1);
547   gst_atomic_int_init (aint, val);
548
549   return aint;
550 }
551
552 GST_INLINE_FUNC void
553 gst_atomic_int_free (GstAtomicInt * aint)
554 {
555   gst_atomic_int_destroy (aint);
556   g_free (aint);
557 }
558
559 #endif /* defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__) */
560
561 G_END_DECLS
562 #endif /*  __GST_ATOMIC_IMPL_H__ */