- copy kernel headers (waiting for libatomic..)
[platform/upstream/gstreamer.git] / gst / gsttrashstack.h
1 /* GStreamer
2  * Copyright (C) <1999> 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 #ifndef __GST_TRASH_STACK_H__
21 #define __GST_TRASH_STACK_H__
22
23 #include <glib.h>
24 #include "gstmacros.h"
25
26 G_BEGIN_DECLS
27
28 typedef struct _GstTrashStack GstTrashStack;
29 typedef struct _GstTrashStackElement GstTrashStackElement;
30
31 struct _GstTrashStackElement {
32   GstTrashStackElement *next;
33 };
34
35 struct _GstTrashStack {
36   volatile gpointer      head;  
37   volatile gulong        count;                 /* for the ABA problem */
38   GMutex                *lock;                  /* lock for C fallback */
39 };
40
41 GST_INLINE_FUNC GstTrashStack*  gst_trash_stack_new     (void);
42 GST_INLINE_FUNC void            gst_trash_stack_init    (GstTrashStack *stack);
43 GST_INLINE_FUNC void            gst_trash_stack_destroy (GstTrashStack *stack);
44 GST_INLINE_FUNC void            gst_trash_stack_free    (GstTrashStack *stack);
45
46 GST_INLINE_FUNC void            gst_trash_stack_push    (GstTrashStack *stack, gpointer mem);
47 GST_INLINE_FUNC gpointer        gst_trash_stack_pop     (GstTrashStack *stack);
48
49 #if defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)
50
51 #if defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2 
52
53 /*
54  * intel ia32 optimized lockfree implementations
55  */
56 GST_INLINE_FUNC void
57 gst_trash_stack_init (GstTrashStack *stack)
58 {
59   stack->head = NULL;
60   stack->count = 0;
61 }
62
63 GST_INLINE_FUNC void
64 gst_trash_stack_destroy (GstTrashStack *stack)
65 {
66 }
67
68 GST_INLINE_FUNC void
69 gst_trash_stack_push (GstTrashStack *stack, gpointer mem)
70 {
71  __asm__ __volatile__ (
72    "1:                         \n\t"
73    "  movl %2, (%1);           \n\t"    /* mem->next == stack->head */
74    "  lock; cmpxchg %1, %0;    \n\t"    /* if head unchanged, move mem into it */
75    "  jnz 1b;                  \n"      /* head changed, retry */
76      :
77      : "m" (*stack),
78        "r" (mem),
79        "a" (stack->head)
80   );
81 }
82
83 GST_INLINE_FUNC gpointer
84 gst_trash_stack_pop (GstTrashStack *stack)
85 {
86   GstTrashStackElement *head;
87
88   /* pop is a little more complicated as we need to avoid the so called ABA
89    * problem that arises when a pop and push of the same element happens
90    * right between when we read head->next and try to swing the new pointer
91    * into place. This is usually solved using a counter which makes it highly
92    * inlikely that we manage to grab the wrong head->next value.
93    */
94   __asm__ __volatile__ (
95     "  testl %%eax, %%eax;      \n\t"   /* if (head == NULL) return */
96     "  jz 20f;                  \n\t"
97     "10:                        \n\t"
98     "  movl (%%eax), %%ebx;     \n\t"   /* take value pointed to by head (head->next) */
99     "  movl %%edx, %%ecx;       \n\t"   /* take counter */
100     "  incl %%ecx;              \n\t"   /* and increment */
101     "  lock; cmpxchg8b %1;      \n\t"   /* if eax:edx == *stack, move ebx:ecx to *stack,
102                                          * else *stack is moved into eax:edx again... */
103     "  jnz 10b;                 \n\t"   /* ... and we retry */
104     "20:                        \n"
105       : "=a" (head)
106       :  "m" (*stack),
107          "a" (stack->head),
108          "d" (stack->count)
109       :  "ecx", "ebx"
110   );
111
112   return head;
113 }
114
115 #else
116
117 /*
118  * generic implementation
119  */
120 GST_INLINE_FUNC void
121 gst_trash_stack_init (GstTrashStack *stack)
122 {
123   stack->head = NULL;
124   stack->lock = g_mutex_new();
125 }
126
127 GST_INLINE_FUNC void
128 gst_trash_stack_destroy (GstTrashStack *stack)
129 {
130   g_mutex_free (stack->lock);
131 }
132
133 GST_INLINE_FUNC void
134 gst_trash_stack_push (GstTrashStack *stack, gpointer mem)
135 {
136   GstTrashStackElement *elem = (GstTrashStackElement *) mem;
137
138   g_mutex_lock (stack->lock);
139   elem->next = stack->head;
140   stack->head = elem;
141   g_mutex_unlock (stack->lock);
142 }
143
144 GST_INLINE_FUNC gpointer
145 gst_trash_stack_pop (GstTrashStack *stack)
146 {
147   GstTrashStackElement *head;
148   
149   g_mutex_lock (stack->lock);
150   head = (GstTrashStackElement *) stack->head;
151   if (head)
152     stack->head = head->next;
153   g_mutex_unlock (stack->lock);
154
155   return head;
156 }
157
158 #endif
159
160 /*
161  * common functions
162  */
163 GST_INLINE_FUNC GstTrashStack*
164 gst_trash_stack_new (void)
165 {
166   GstTrashStack *stack;
167
168   stack = g_new (GstTrashStack, 1);
169   gst_trash_stack_init (stack);
170
171   return stack;
172 }
173
174 GST_INLINE_FUNC void
175 gst_trash_stack_free (GstTrashStack *stack)
176 {
177   gst_trash_stack_destroy (stack);
178   g_free (stack);
179 }
180
181 #endif /* defined (G_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)*/
182
183 G_END_DECLS
184
185 #endif /*  __GST_TRASH_STACK_H__ */