fix doc build fix autogen
[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
38 #if defined (GST_CAN_INLINE) || defined (__GST_ATOMIC_C__)
39   
40 /***** Intel x86 *****/
41 #if defined (HAVE_CPU_I386) && defined(__GNUC__)
42
43 #ifdef GST_CONFIG_NO_SMP
44 #define SMP_LOCK ""
45 #else
46 #define SMP_LOCK "lock ; "
47 #endif
48
49 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
50 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
51 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
52 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
53
54 GST_INLINE_FUNC void 
55 gst_atomic_int_add (GstAtomicInt *aint, gint val)
56 {
57   __asm__ __volatile__(
58     SMP_LOCK "addl %1,%0"
59       :"=m" (aint->counter)
60       :"ir" (val), "m" (aint->counter));
61 }
62
63 GST_INLINE_FUNC void
64 gst_atomic_int_inc (GstAtomicInt *aint)
65 {
66   __asm__ __volatile__(
67     SMP_LOCK "incl %0"
68       :"=m" (aint->counter)
69       :"m" (aint->counter));
70 }
71
72 GST_INLINE_FUNC gboolean
73 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
74 {
75   guchar res;
76
77   __asm__ __volatile__(
78     SMP_LOCK "decl %0; sete %1"
79       :"=m" (aint->counter), "=qm" (res)
80       :"m" (aint->counter) : "memory");
81
82   return res != 0;
83 }
84
85 /***** PowerPC *****/
86 #elif defined (HAVE_CPU_PPC) && defined(__GNUC__)
87
88 #ifdef GST_CONFIG_NO_SMP
89 #define SMP_SYNC        ""
90 #define SMP_ISYNC
91 #else
92 #define SMP_SYNC        "\tsync\n"
93 #define SMP_ISYNC       "\tisync\n"
94 #endif
95
96 /* Erratum #77 on the 405 means we need a sync or dcbt before every stwcx.
97  * The old ATOMIC_SYNC_FIX covered some but not all of this.
98  */
99 #ifdef GST_CONFIG_IBM405_ERR77
100 #define PPC405_ERR77(ra,rb)     "\tdcbt " #ra "," #rb "\n"
101 #else
102 #define PPC405_ERR77(ra,rb)
103 #endif
104
105 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
106 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
107 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
108 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
109
110 GST_INLINE_FUNC void 
111 gst_atomic_int_add (GstAtomicInt *aint, gint val)
112 {
113   int t;
114
115   __asm__ __volatile__(
116     "1:     lwarx   %0,0,%3\n"
117     "       add     %0,%2,%0\n"
118             PPC405_ERR77(0,%3)
119     "       stwcx.  %0,0,%3 \n"
120     "       bne-    1b\n"
121       : "=&r" (t), "=m" (aint->counter)
122       : "r" (val), "r" (&aint->counter), "m" (aint->counter)
123       : "cc");
124 }
125
126 GST_INLINE_FUNC void
127 gst_atomic_int_inc (GstAtomicInt *aint)
128 {
129   int t;
130
131   __asm__ __volatile__(
132     "1:     lwarx   %0,0,%2\n"
133     "       addic   %0,%0,1\n"
134             PPC405_ERR77(0,%2)
135     "       stwcx.  %0,0,%2\n"
136     "       bne-    1b\n"
137       : "=&r" (t), "=m" (aint->counter)
138       : "r" (&aint->counter), "m" (aint->counter)
139       : "cc");
140 }
141
142 GST_INLINE_FUNC gboolean
143 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
144 {
145   int t;
146
147   __asm__ __volatile__(
148     "1:     lwarx   %0,0,%1\n"
149     "       addic   %0,%0,-1\n"
150             PPC405_ERR77(0,%1)
151     "       stwcx.  %0,0,%1\n"
152     "       bne-    1b\n"
153             SMP_ISYNC
154       : "=&r" (t)
155       : "r" (&aint->counter)
156       : "cc", "memory");
157
158   return t == 0;
159 }
160
161 /***** DEC[/Compaq/HP?/Intel?] Alpha *****/
162 #elif defined(HAVE_CPU_ALPHA) && defined(__GNUC__)
163
164 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
165 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
166 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
167 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
168
169 GST_INLINE_FUNC void 
170 gst_atomic_int_add (GstAtomicInt *aint, gint val)
171 {
172   unsigned long temp;
173
174   __asm__ __volatile__(
175     "1:     ldl_l %0,%1\n"
176     "       addl %0,%2,%0\n"
177     "       stl_c %0,%1\n"
178     "       beq %0,2f\n"
179     ".subsection 2\n"
180     "2:     br 1b\n"
181     ".previous"
182       :"=&r" (temp), "=m" (aint->counter)
183       :"Ir" (val), "m" (aint->counter));
184 }
185
186 GST_INLINE_FUNC void
187 gst_atomic_int_inc (GstAtomicInt *aint)
188 {
189   gst_atomic_int_add (aint, 1);
190 }
191
192 GST_INLINE_FUNC gboolean
193 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
194 {
195   long temp, result;
196   int val = 1;
197   __asm__ __volatile__(
198     "1:     ldl_l %0,%1\n"
199     "       subl %0,%3,%2\n"
200     "       subl %0,%3,%0\n"
201     "       stl_c %0,%1\n"
202     "       beq %0,2f\n"
203     "       mb\n"
204     ".subsection 2\n"
205     "2:     br 1b\n"
206     ".previous"
207       :"=&r" (temp), "=m" (aint->counter), "=&r" (result)
208       :"Ir" (val), "m" (aint->counter) : "memory");
209
210   return result == 0;
211 }
212
213 /***** Sun SPARC *****/
214 #elif defined(HAVE_CPU_SPARC) && defined(__GNUC__)
215
216 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
217
218 #ifdef GST_CONFIG_NO_SMP
219 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
220 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
221 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
222 #else
223 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = (val<<8); }
224 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = (val<<8); }
225
226 /*
227  * For SMP the trick is you embed the spin lock byte within
228  * the word, use the low byte so signedness is easily retained
229  * via a quick arithmetic shift.  It looks like this:
230  *
231  *      ----------------------------------------
232  *      | signed 24-bit counter value |  lock  |  atomic_t
233  *      ----------------------------------------
234  *       31                          8 7      0
235  */
236 GST_INLINE_FUNC gint
237 gst_atomic_int_read (GstAtomicInt *aint) 
238
239   int ret = aint->counter;
240
241   while (ret & 0xff)
242     ret = aint->counter;
243
244   return ret >> 8;
245 }
246 #endif /* GST_CONFIG_NO_SMP */
247
248 GST_INLINE_FUNC void 
249 gst_atomic_int_add (GstAtomicInt *aint, gint val)
250 {
251   volatile int increment, *ptr;
252   int lock = 1;
253   int ignore = 0;
254
255   ptr = &(aint->counter);
256
257 #if __GNUC__ > 3 || (__GNUC__ >=3 && __GNUC_MINOR__ >= 2)
258  __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
259                       "\torcc %[lock], 0, %[ignore]\n"
260                       "\tbne 1b\n" /* go back until we have the lock */
261                       "\tld [%[ptr]], %[inc]\n"
262                       "\tsra %[inc], 8, %[inc]\n"
263                       "\tadd %[inc], %[val], %[inc]\n"
264                       "\tsll %[inc], 8, %[lock]\n"
265                       "\tst %[lock],[%[ptr]]\n" /* Release the lock */
266                       : [inc] "=&r" (increment), [lock] "=r" (lock),
267                         [ignore] "=&r" (ignore)
268                       : "0" (increment), [ptr] "r" (ptr), [val] "r" (val)
269                       );
270 #else
271  __asm__ __volatile__("1: ldstub [%4 + 3], %1\n"
272                       "\torcc %1, 0, %2\n"
273                       "\tbne 1b\n" /* go back until we have the lock */
274                       "\tld [%4], %0\n"
275                       "\tsra %0, 8, %0\n"
276                       "\tadd %0, %5, %0\n"
277                       "\tsll %0, 8, %1\n"
278                       "\tst %1,[%4]\n" /* Release the lock */
279                       : "=&r" (increment), "=r" (lock), "=&r" (ignore)
280                       : "0" (increment), "r" (ptr), "r" (val)
281                       );
282 #endif
283 }
284
285 GST_INLINE_FUNC void
286 gst_atomic_int_inc (GstAtomicInt *aint)
287 {
288   gst_atomic_int_add (aint, 1);
289 }
290
291 GST_INLINE_FUNC gboolean
292 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
293 {
294   volatile int increment, *ptr;
295   int lock = 1;
296   int ignore = 0;
297
298   ptr = &aint->counter;
299
300 #if __GNUC__ > 3 || (__GNUC__ >=3 && __GNUC_MINOR__ >= 2)
301   __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
302                        "\torcc %[lock], 0, %[ignore]\n"
303                        "\tbne 1b\n" /* go back until we have the lock */
304                        "\tld [%[ptr]], %[inc]\n"
305                        "\tsra %[inc], 8, %[inc]\n"
306                        "\tsub %[inc], 1, %[inc]\n"
307                        "\tsll %[inc], 8, %[lock]\n"
308                        "\tst %[lock],[%[ptr]]\n" /* Release the lock */
309                        : [inc] "=&r" (increment), [lock] "=r" (lock),
310                          [ignore] "=&r" (ignore)
311                        : "0" (increment), [ptr] "r" (ptr)
312                        );
313 #else
314   __asm__ __volatile__("1: ldstub [%4 + 3], %1\n"
315                        "\torcc %1, 0, %2\n"
316                        "\tbne 1b\n" /* go back until we have the lock */
317                        "\tld [%4], %0\n"
318                        "\tsra %0, 8, %0\n"
319                        "\tsub %0, 1, %0\n"
320                        "\tsll %0, 8, %1\n"
321                        "\tst %1,[%4]\n" /* Release the lock */
322                        : "=&r" (increment), "=r" (lock), "=&r" (ignore)
323                        : "0" (increment), "r" (ptr)
324                        );
325 #endif
326
327   return increment == 0;
328 }
329
330 /***** MIPS *****/
331 /* This is disabled because the asm code is broken on most MIPS
332  * processors and doesn't generally compile. */
333 #elif defined(HAVE_CPU_MIPS) && defined(__GNUC__) && 0
334
335 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
336 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
337 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
338 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
339
340 /* this only works on MIPS II and better */
341 GST_INLINE_FUNC void 
342 gst_atomic_int_add (GstAtomicInt *aint, gint val)
343 {
344   unsigned long temp;
345
346   __asm__ __volatile__(
347     "1:   ll      %0, %1      # atomic_add\n"
348     "     addu    %0, %2                  \n"
349     "     sc      %0, %1                  \n"
350     "     beqz    %0, 1b                  \n"
351       : "=&r" (temp), "=m" (aint->counter)
352       : "Ir" (val), "m" (aint->counter));
353 }
354
355 GST_INLINE_FUNC void
356 gst_atomic_int_inc (GstAtomicInt *aint)
357 {
358   gst_atomic_int_add (aint, 1);
359 }
360
361 GST_INLINE_FUNC gboolean
362 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
363 {
364   unsigned long temp, result;
365   int val = 1;
366
367   __asm__ __volatile__(
368     ".set push                                   \n"
369     ".set noreorder           # atomic_sub_return\n"
370     "1:   ll    %1, %2                           \n"
371     "     subu  %0, %1, %3                       \n"
372     "     sc    %0, %2                           \n"
373     "     beqz  %0, 1b                           \n"
374     "     subu  %0, %1, %3                       \n"
375     ".set pop                                    \n"
376       : "=&r" (result), "=&r" (temp), "=m" (aint->counter)
377       : "Ir" (val), "m" (aint->counter)
378       : "memory");
379
380   return result == 0;
381 }
382
383 /***** S/390 *****/
384 #elif defined(HAVE_CPU_S390) && defined(__GNUC__)
385
386 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
387 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
388 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
389 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
390
391 #define __CS_LOOP(old_val, new_val, ptr, op_val, op_string)             \
392         __asm__ __volatile__("   l     %0,0(%3)\n"                      \
393                              "0: lr    %1,%0\n"                         \
394                              op_string "  %1,%4\n"                      \
395                              "   cs    %0,%1,0(%3)\n"                   \
396                              "   jl    0b"                              \
397                              : "=&d" (old_val), "=&d" (new_val),        \
398                                "+m" (((atomic_t *)(ptr))->counter)      \
399                              : "a" (ptr), "d" (op_val) : "cc" );
400
401 GST_INLINE_FUNC void 
402 gst_atomic_int_add (GstAtomicInt *aint, gint val)
403 {
404   int old_val, new_val;
405   __CS_LOOP(old_val, new_val, aint, val, "ar");
406 }
407
408 GST_INLINE_FUNC void
409 gst_atomic_int_inc (GstAtomicInt *aint)
410 {
411   int old_val, new_val;
412   __CS_LOOP(old_val, new_val, aint, 1, "ar");
413 }
414
415 GST_INLINE_FUNC gboolean
416 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
417 {
418   int old_val, new_val;
419   __CS_LOOP(old_val, new_val, aint, 1, "sr");
420   return new_val == 0;
421 }
422
423 #else 
424 #warning consider putting your architecture specific atomic implementations here
425
426 /*
427  * generic implementation
428  */
429 GST_INLINE_FUNC void
430 gst_atomic_int_init (GstAtomicInt *aint, gint val)
431 {
432   aint->counter = val;
433   aint->lock = g_mutex_new ();
434 }
435
436 GST_INLINE_FUNC void
437 gst_atomic_int_destroy (GstAtomicInt *aint)
438 {
439   g_mutex_free (aint->lock);
440 }
441
442 GST_INLINE_FUNC void
443 gst_atomic_int_set (GstAtomicInt *aint, gint val)
444 {
445   g_mutex_lock (aint->lock);
446   aint->counter = val;
447   g_mutex_unlock (aint->lock);
448 }
449
450 GST_INLINE_FUNC gint
451 gst_atomic_int_read (GstAtomicInt *aint)
452 {
453   gint res;
454
455   g_mutex_lock (aint->lock);
456   res = aint->counter;
457   g_mutex_unlock (aint->lock);
458
459   return res;
460 }
461
462 GST_INLINE_FUNC void 
463 gst_atomic_int_add (GstAtomicInt *aint, gint val)
464 {
465   g_mutex_lock (aint->lock);
466   aint->counter += val;
467   g_mutex_unlock (aint->lock);
468 }
469
470 GST_INLINE_FUNC void
471 gst_atomic_int_inc (GstAtomicInt *aint)
472 {
473   g_mutex_lock (aint->lock);
474   aint->counter++;
475   g_mutex_unlock (aint->lock);
476 }
477
478 GST_INLINE_FUNC gboolean
479 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
480 {
481   gboolean res;
482   
483   g_mutex_lock (aint->lock);
484   aint->counter--;
485   res = (aint->counter == 0);
486   g_mutex_unlock (aint->lock);
487
488   return res;
489 }
490
491 #endif 
492 /*
493  * common functions
494  */ 
495 GST_INLINE_FUNC GstAtomicInt*
496 gst_atomic_int_new (gint val)
497 {
498   GstAtomicInt *aint;
499
500   aint = g_new0 (GstAtomicInt, 1);
501   gst_atomic_int_init (aint, val);
502
503   return aint;
504 }
505
506 GST_INLINE_FUNC void
507 gst_atomic_int_free (GstAtomicInt *aint)
508 {
509   gst_atomic_int_destroy (aint);
510   g_free (aint);
511 }
512
513 #endif /* defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)*/
514
515 G_END_DECLS
516
517 #endif /*  __GST_ATOMIC_IMPL_H__ */