fix sparc atomic functions.
[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        "sync"
93 #define SMP_ISYNC       "\n\tisync"
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)     "dcbt " #ra "," #rb ";"
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         # atomic_add\n\
117             add     %0,%2,%0\n"
118             PPC405_ERR77(0,%3)
119     "       stwcx.  %0,0,%3 \n\
120             bne-    1b"
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         # atomic_inc\n\
133             addic   %0,%0,1\n"
134             PPC405_ERR77(0,%2)
135     "       stwcx.  %0,0,%2 \n\
136             bne-    1b"
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         # atomic_dec_return\n\
149             addic   %0,%0,-1\n"
150             PPC405_ERR77(0,%1)
151     "       stwcx.  %0,0,%1\n\
152             bne-    1b"
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   char lock;
253
254   ptr = &(aint->counter);
255
256  __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
257                       "\torcc %[lock], 0, %%g0\n"
258                       "\tbne 1b\n" /* go back until we have the lock */
259                       "\tld [%[ptr]], %[inc]\n"
260                       "\tsra %[inc], 8, %[inc]\n"
261                       "\tadd %[inc], %[val], %[inc]\n"
262                       "\tsll %[inc], 8, %[lock]\n"
263                       "\tst %[lock],[%[ptr]]\n" /* Release the lock */
264                       : [inc] "=&r" (increment), [lock] "=r" (lock)
265                       : "0" (increment), [ptr] "r" (ptr), [val] "r" (val)
266                       );
267 }
268
269 GST_INLINE_FUNC void
270 gst_atomic_int_inc (GstAtomicInt *aint)
271 {
272   gst_atomic_int_add (aint, 1);
273 }
274
275 GST_INLINE_FUNC gboolean
276 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
277 {
278   volatile int increment, *ptr;
279   char lock;
280
281   ptr = &aint->counter;
282
283   __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
284                        "\torcc %[lock], 0, %%g0\n"
285                        "\tbne 1b\n" /* go back until we have the lock */
286                        "\tld [%[ptr]], %[inc]\n"
287                        "\tsra %[inc], 8, %[inc]\n"
288                        "\tsub %[inc], 1, %[inc]\n"
289                        "\tsll %[inc], 8, %[lock]\n"
290                        "\tst %[lock],[%[ptr]]\n" /* Release the lock */
291                        : [inc] "=&r" (increment), [lock] "=r" (lock)
292                        : "0" (increment), [ptr] "r" (ptr)
293                        );
294
295   return increment == 0;
296 }
297
298 /***** MIPS *****/
299 /* This is disabled because the asm code is broken on most MIPS
300  * processors and doesn't generally compile. */
301 #elif defined(HAVE_CPU_MIPS) && defined(__GNUC__) && 0
302
303 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
304 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
305 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
306 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
307
308 /* this only works on MIPS II and better */
309 GST_INLINE_FUNC void 
310 gst_atomic_int_add (GstAtomicInt *aint, gint val)
311 {
312   unsigned long temp;
313
314   __asm__ __volatile__(
315     "1:   ll      %0, %1      # atomic_add\n"
316     "     addu    %0, %2                  \n"
317     "     sc      %0, %1                  \n"
318     "     beqz    %0, 1b                  \n"
319       : "=&r" (temp), "=m" (aint->counter)
320       : "Ir" (val), "m" (aint->counter));
321 }
322
323 GST_INLINE_FUNC void
324 gst_atomic_int_inc (GstAtomicInt *aint)
325 {
326   gst_atomic_int_add (aint, 1);
327 }
328
329 GST_INLINE_FUNC gboolean
330 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
331 {
332   unsigned long temp, result;
333   int val = 1;
334
335   __asm__ __volatile__(
336     ".set push                                   \n"
337     ".set noreorder           # atomic_sub_return\n"
338     "1:   ll    %1, %2                           \n"
339     "     subu  %0, %1, %3                       \n"
340     "     sc    %0, %2                           \n"
341     "     beqz  %0, 1b                           \n"
342     "     subu  %0, %1, %3                       \n"
343     ".set pop                                    \n"
344       : "=&r" (result), "=&r" (temp), "=m" (aint->counter)
345       : "Ir" (val), "m" (aint->counter)
346       : "memory");
347
348   return result == 0;
349 }
350
351 /***** S/390 *****/
352 #elif defined(HAVE_CPU_S390) && defined(__GNUC__)
353
354 GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
355 GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { } 
356 GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
357 GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
358
359 #define __CS_LOOP(old_val, new_val, ptr, op_val, op_string)             \
360         __asm__ __volatile__("   l     %0,0(%3)\n"                      \
361                              "0: lr    %1,%0\n"                         \
362                              op_string "  %1,%4\n"                      \
363                              "   cs    %0,%1,0(%3)\n"                   \
364                              "   jl    0b"                              \
365                              : "=&d" (old_val), "=&d" (new_val),        \
366                                "+m" (((atomic_t *)(ptr))->counter)      \
367                              : "a" (ptr), "d" (op_val) : "cc" );
368
369 GST_INLINE_FUNC void 
370 gst_atomic_int_add (GstAtomicInt *aint, gint val)
371 {
372   int old_val, new_val;
373   __CS_LOOP(old_val, new_val, aint, val, "ar");
374 }
375
376 GST_INLINE_FUNC void
377 gst_atomic_int_inc (GstAtomicInt *aint)
378 {
379   int old_val, new_val;
380   __CS_LOOP(old_val, new_val, aint, 1, "ar");
381 }
382
383 GST_INLINE_FUNC gboolean
384 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
385 {
386   int old_val, new_val;
387   __CS_LOOP(old_val, new_val, aint, 1, "sr");
388   return new_val == 0;
389 }
390
391 #else 
392 #warning consider putting your architecture specific atomic implementations here
393
394 /*
395  * generic implementation
396  */
397 GST_INLINE_FUNC void
398 gst_atomic_int_init (GstAtomicInt *aint, gint val)
399 {
400   aint->counter = val;
401   aint->lock = g_mutex_new ();
402 }
403
404 GST_INLINE_FUNC void
405 gst_atomic_int_destroy (GstAtomicInt *aint)
406 {
407   g_mutex_free (aint->lock);
408 }
409
410 GST_INLINE_FUNC void
411 gst_atomic_int_set (GstAtomicInt *aint, gint val)
412 {
413   g_mutex_lock (aint->lock);
414   aint->counter = val;
415   g_mutex_unlock (aint->lock);
416 }
417
418 GST_INLINE_FUNC gint
419 gst_atomic_int_read (GstAtomicInt *aint)
420 {
421   gint res;
422
423   g_mutex_lock (aint->lock);
424   res = aint->counter;
425   g_mutex_unlock (aint->lock);
426
427   return res;
428 }
429
430 GST_INLINE_FUNC void 
431 gst_atomic_int_add (GstAtomicInt *aint, gint val)
432 {
433   g_mutex_lock (aint->lock);
434   aint->counter += val;
435   g_mutex_unlock (aint->lock);
436 }
437
438 GST_INLINE_FUNC void
439 gst_atomic_int_inc (GstAtomicInt *aint)
440 {
441   g_mutex_lock (aint->lock);
442   aint->counter++;
443   g_mutex_unlock (aint->lock);
444 }
445
446 GST_INLINE_FUNC gboolean
447 gst_atomic_int_dec_and_test (GstAtomicInt *aint)
448 {
449   gboolean res;
450   
451   g_mutex_lock (aint->lock);
452   aint->counter--;
453   res = (aint->counter == 0);
454   g_mutex_unlock (aint->lock);
455
456   return res;
457 }
458
459 #endif 
460 /*
461  * common functions
462  */ 
463 GST_INLINE_FUNC GstAtomicInt*
464 gst_atomic_int_new (gint val)
465 {
466   GstAtomicInt *aint;
467
468   aint = g_new0 (GstAtomicInt, 1);
469   gst_atomic_int_init (aint, val);
470
471   return aint;
472 }
473
474 GST_INLINE_FUNC void
475 gst_atomic_int_free (GstAtomicInt *aint)
476 {
477   gst_atomic_int_destroy (aint);
478   g_free (aint);
479 }
480
481 #endif /* defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)*/
482
483 G_END_DECLS
484
485 #endif /*  __GST_ATOMIC_IMPL_H__ */