source: trunk/third/gstreamer/gst/gstatomic_impl.h @ 21009

Revision 21009, 14.4 KB checked in by ghudson, 20 years ago (diff)
Merge with gstreamer 0.8.5.
Line 
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
36G_BEGIN_DECLS
37
38#if defined (GST_CAN_INLINE) || defined (__GST_ATOMIC_C__)
39 
40/***** Intel x86 *****/
41#if (defined (HAVE_CPU_I386) || defined (HAVE_CPU_X86_64)) && defined(__GNUC__)
42
43#ifdef GST_CONFIG_NO_SMP
44#define SMP_LOCK ""
45#else
46#define SMP_LOCK "lock ; "
47#endif
48
49GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
50GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { }
51GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
52GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
53
54GST_INLINE_FUNC void
55gst_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
63GST_INLINE_FUNC void
64gst_atomic_int_inc (GstAtomicInt *aint)
65{
66  __asm__ __volatile__(
67    SMP_LOCK "incl %0"
68      :"=m" (aint->counter)
69      :"m" (aint->counter));
70}
71
72GST_INLINE_FUNC gboolean
73gst_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
105GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
106GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { }
107GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
108GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
109
110GST_INLINE_FUNC void
111gst_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
126GST_INLINE_FUNC void
127gst_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
142GST_INLINE_FUNC gboolean
143gst_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
164GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
165GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { }
166GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
167GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
168
169GST_INLINE_FUNC void
170gst_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
186GST_INLINE_FUNC void
187gst_atomic_int_inc (GstAtomicInt *aint)
188{
189  gst_atomic_int_add (aint, 1);
190}
191
192GST_INLINE_FUNC gboolean
193gst_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 0 && defined(HAVE_CPU_SPARC) && defined(__GNUC__)
215/* allegedly broken again */
216
217GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { }
218
219#ifdef GST_CONFIG_NO_SMP
220GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
221GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
222GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
223#else
224GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = (val<<8); }
225GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = (val<<8); }
226
227/*
228 * For SMP the trick is you embed the spin lock byte within
229 * the word, use the low byte so signedness is easily retained
230 * via a quick arithmetic shift.  It looks like this:
231 *
232 *      ----------------------------------------
233 *      | signed 24-bit counter value |  lock  |  atomic_t
234 *      ----------------------------------------
235 *       31                          8 7      0
236 */
237GST_INLINE_FUNC gint
238gst_atomic_int_read (GstAtomicInt *aint)
239{
240  int ret = aint->counter;
241
242  while (ret & 0xff)
243    ret = aint->counter;
244
245  return ret >> 8;
246}
247#endif /* GST_CONFIG_NO_SMP */
248
249GST_INLINE_FUNC void
250gst_atomic_int_add (GstAtomicInt *aint, gint val)
251{
252  volatile int increment, *ptr;
253  int lock = 1;
254  int ignore = 0;
255
256  ptr = &(aint->counter);
257
258#if __GNUC__ > 3 || (__GNUC__ >=3 && __GNUC_MINOR__ >= 2)
259 __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
260                      "\torcc %[lock], 0, %[ignore]\n"
261                      "\tbne 1b\n" /* go back until we have the lock */
262                      "\tld [%[ptr]], %[inc]\n"
263                      "\tsra %[inc], 8, %[inc]\n"
264                      "\tadd %[inc], %[val], %[inc]\n"
265                      "\tsll %[inc], 8, %[lock]\n"
266                      "\tst %[lock],[%[ptr]]\n" /* Release the lock */
267                      : [inc] "=&r" (increment), [lock] "=r" (lock),
268                        [ignore] "=&r" (ignore)
269                      : "0" (increment), [ptr] "r" (ptr), [val] "r" (val)
270                      );
271#else
272 __asm__ __volatile__("1: ldstub [%4 + 3], %1\n"
273                      "\torcc %1, 0, %2\n"
274                      "\tbne 1b\n" /* go back until we have the lock */
275                      "\tld [%4], %0\n"
276                      "\tsra %0, 8, %0\n"
277                      "\tadd %0, %5, %0\n"
278                      "\tsll %0, 8, %1\n"
279                      "\tst %1,[%4]\n" /* Release the lock */
280                      : "=&r" (increment), "=r" (lock), "=&r" (ignore)
281                      : "0" (increment), "r" (ptr), "r" (val)
282                      );
283#endif
284}
285
286GST_INLINE_FUNC void
287gst_atomic_int_inc (GstAtomicInt *aint)
288{
289  gst_atomic_int_add (aint, 1);
290}
291
292GST_INLINE_FUNC gboolean
293gst_atomic_int_dec_and_test (GstAtomicInt *aint)
294{
295  volatile int increment, *ptr;
296  int lock = 1;
297  int ignore = 0;
298
299  ptr = &aint->counter;
300
301#if __GNUC__ > 3 || (__GNUC__ >=3 && __GNUC_MINOR__ >= 2)
302  __asm__ __volatile__("1: ldstub [%[ptr] + 3], %[lock]\n"
303                       "\torcc %[lock], 0, %[ignore]\n"
304                       "\tbne 1b\n" /* go back until we have the lock */
305                       "\tld [%[ptr]], %[inc]\n"
306                       "\tsra %[inc], 8, %[inc]\n"
307                       "\tsub %[inc], 1, %[inc]\n"
308                       "\tsll %[inc], 8, %[lock]\n"
309                       "\tst %[lock],[%[ptr]]\n" /* Release the lock */
310                       : [inc] "=&r" (increment), [lock] "=r" (lock),
311                         [ignore] "=&r" (ignore)
312                       : "0" (increment), [ptr] "r" (ptr)
313                       );
314#else
315  __asm__ __volatile__("1: ldstub [%4 + 3], %1\n"
316                       "\torcc %1, 0, %2\n"
317                       "\tbne 1b\n" /* go back until we have the lock */
318                       "\tld [%4], %0\n"
319                       "\tsra %0, 8, %0\n"
320                       "\tsub %0, 1, %0\n"
321                       "\tsll %0, 8, %1\n"
322                       "\tst %1,[%4]\n" /* Release the lock */
323                       : "=&r" (increment), "=r" (lock), "=&r" (ignore)
324                       : "0" (increment), "r" (ptr)
325                       );
326#endif
327
328  return increment == 0;
329}
330
331/***** MIPS *****/
332/* This is disabled because the asm code is broken on most MIPS
333 * processors and doesn't generally compile. */
334#elif defined(HAVE_CPU_MIPS) && defined(__GNUC__) && 0
335
336GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
337GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { }
338GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
339GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
340
341/* this only works on MIPS II and better */
342GST_INLINE_FUNC void
343gst_atomic_int_add (GstAtomicInt *aint, gint val)
344{
345  unsigned long temp;
346
347  __asm__ __volatile__(
348    "1:   ll      %0, %1      # atomic_add\n"
349    "     addu    %0, %2                  \n"
350    "     sc      %0, %1                  \n"
351    "     beqz    %0, 1b                  \n"
352      : "=&r" (temp), "=m" (aint->counter)
353      : "Ir" (val), "m" (aint->counter));
354}
355
356GST_INLINE_FUNC void
357gst_atomic_int_inc (GstAtomicInt *aint)
358{
359  gst_atomic_int_add (aint, 1);
360}
361
362GST_INLINE_FUNC gboolean
363gst_atomic_int_dec_and_test (GstAtomicInt *aint)
364{
365  unsigned long temp, result;
366  int val = 1;
367
368  __asm__ __volatile__(
369    ".set push                                   \n"
370    ".set noreorder           # atomic_sub_return\n"
371    "1:   ll    %1, %2                           \n"
372    "     subu  %0, %1, %3                       \n"
373    "     sc    %0, %2                           \n"
374    "     beqz  %0, 1b                           \n"
375    "     subu  %0, %1, %3                       \n"
376    ".set pop                                    \n"
377      : "=&r" (result), "=&r" (temp), "=m" (aint->counter)
378      : "Ir" (val), "m" (aint->counter)
379      : "memory");
380
381  return result == 0;
382}
383
384/***** S/390 *****/
385#elif defined(HAVE_CPU_S390) && defined(__GNUC__)
386typedef struct { volatile int counter; } atomic_t __attribute__ ((aligned (4)));
387
388GST_INLINE_FUNC void    gst_atomic_int_init     (GstAtomicInt *aint, gint val) { aint->counter = val; }
389GST_INLINE_FUNC void    gst_atomic_int_destroy  (GstAtomicInt *aint) { }
390GST_INLINE_FUNC void    gst_atomic_int_set      (GstAtomicInt *aint, gint val) { aint->counter = val; }
391GST_INLINE_FUNC gint    gst_atomic_int_read     (GstAtomicInt *aint) { return aint->counter; }
392
393#define __CS_LOOP(old_val, new_val, ptr, op_val, op_string)             \
394        __asm__ __volatile__("   l     %0,0(%3)\n"                      \
395                             "0: lr    %1,%0\n"                         \
396                             op_string "  %1,%4\n"                      \
397                             "   cs    %0,%1,0(%3)\n"                   \
398                             "   jl    0b"                              \
399                             : "=&d" (old_val), "=&d" (new_val),        \
400                               "+m" (((atomic_t *)(ptr))->counter)      \
401                             : "a" (ptr), "d" (op_val) : "cc" );
402
403GST_INLINE_FUNC void
404gst_atomic_int_add (GstAtomicInt *aint, gint val)
405{
406  int old_val, new_val;
407  __CS_LOOP(old_val, new_val, aint, val, "ar");
408}
409
410GST_INLINE_FUNC void
411gst_atomic_int_inc (GstAtomicInt *aint)
412{
413  int old_val, new_val;
414  __CS_LOOP(old_val, new_val, aint, 1, "ar");
415}
416
417GST_INLINE_FUNC gboolean
418gst_atomic_int_dec_and_test (GstAtomicInt *aint)
419{
420  int old_val, new_val;
421  __CS_LOOP(old_val, new_val, aint, 1, "sr");
422  return new_val == 0;
423}
424
425#else
426
427/* no need warning about this if we can't do inline assembly */
428#ifdef __GNUC__
429#warning consider putting your architecture specific atomic implementations here
430#endif
431
432/*
433 * generic implementation
434 */
435GST_INLINE_FUNC void
436gst_atomic_int_init (GstAtomicInt *aint, gint val)
437{
438  aint->counter = val;
439  aint->lock = g_mutex_new ();
440}
441
442GST_INLINE_FUNC void
443gst_atomic_int_destroy (GstAtomicInt *aint)
444{
445  g_mutex_free (aint->lock);
446}
447
448GST_INLINE_FUNC void
449gst_atomic_int_set (GstAtomicInt *aint, gint val)
450{
451  g_mutex_lock (aint->lock);
452  aint->counter = val;
453  g_mutex_unlock (aint->lock);
454}
455
456GST_INLINE_FUNC gint
457gst_atomic_int_read (GstAtomicInt *aint)
458{
459  gint res;
460
461  g_mutex_lock (aint->lock);
462  res = aint->counter;
463  g_mutex_unlock (aint->lock);
464
465  return res;
466}
467
468GST_INLINE_FUNC void
469gst_atomic_int_add (GstAtomicInt *aint, gint val)
470{
471  g_mutex_lock (aint->lock);
472  aint->counter += val;
473  g_mutex_unlock (aint->lock);
474}
475
476GST_INLINE_FUNC void
477gst_atomic_int_inc (GstAtomicInt *aint)
478{
479  g_mutex_lock (aint->lock);
480  aint->counter++;
481  g_mutex_unlock (aint->lock);
482}
483
484GST_INLINE_FUNC gboolean
485gst_atomic_int_dec_and_test (GstAtomicInt *aint)
486{
487  gboolean res;
488 
489  g_mutex_lock (aint->lock);
490  aint->counter--;
491  res = (aint->counter == 0);
492  g_mutex_unlock (aint->lock);
493
494  return res;
495}
496
497#endif
498/*
499 * common functions
500 */
501GST_INLINE_FUNC GstAtomicInt*
502gst_atomic_int_new (gint val)
503{
504  GstAtomicInt *aint;
505
506  aint = g_new0 (GstAtomicInt, 1);
507  gst_atomic_int_init (aint, val);
508
509  return aint;
510}
511
512GST_INLINE_FUNC void
513gst_atomic_int_free (GstAtomicInt *aint)
514{
515  gst_atomic_int_destroy (aint);
516  g_free (aint);
517}
518
519#endif /* defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)*/
520
521G_END_DECLS
522
523#endif /*  __GST_ATOMIC_IMPL_H__ */
Note: See TracBrowser for help on using the repository browser.