First THREADED backport attempt, focusing on adding locks and making sure the API...
[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 typedef volatile gpointer gst_vgpointer;/* gtk-doc volatile workaround */
36 typedef volatile gulong gst_vgulong;    /* gtk-doc volatile workaround */
37                                                                                 
38 struct _GstTrashStack {
39   gst_vgpointer         head;  
40   gst_vgulong           count;          /* for the ABA problem */
41   GMutex                *lock;          /* lock for C fallback */
42 };
43
44 GST_INLINE_FUNC GstTrashStack*  gst_trash_stack_new     (void);
45 GST_INLINE_FUNC void            gst_trash_stack_init    (GstTrashStack *stack);
46 GST_INLINE_FUNC void            gst_trash_stack_destroy (GstTrashStack *stack);
47 GST_INLINE_FUNC void            gst_trash_stack_free    (GstTrashStack *stack);
48
49 GST_INLINE_FUNC void            gst_trash_stack_push    (GstTrashStack *stack, gpointer mem);
50 GST_INLINE_FUNC gpointer        gst_trash_stack_pop     (GstTrashStack *stack);
51
52 #if defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)
53
54 #if defined (USE_FAST_STACK_TRASH) && defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2 
55
56 #ifdef GST_CONFIG_NO_SMP
57 #define SMP_LOCK ""
58 #else
59 #define SMP_LOCK "lock ; "
60 #endif
61
62 /*
63  * intel ia32 optimized lockfree implementations
64  */
65 GST_INLINE_FUNC void
66 gst_trash_stack_init (GstTrashStack *stack)
67 {
68   stack->head = NULL;
69   stack->count = 0;
70 }
71
72 GST_INLINE_FUNC void
73 gst_trash_stack_destroy (GstTrashStack *stack)
74 {
75 }
76
77 GST_INLINE_FUNC void
78 gst_trash_stack_push (GstTrashStack *stack, gpointer mem)
79 {
80  __asm__ __volatile__ (
81    "1:                         \n\t"
82    "  movl %2, (%1);           \n\t"    /* mem->next == stack->head */
83    SMP_LOCK "cmpxchg %1, %0;   \n\t"    /* if head unchanged, move mem into it */
84    "  jnz 1b;                  \n"      /* head changed, retry */
85      :
86      : "m" (*stack),
87        "r" (mem),
88        "a" (stack->head)
89   );
90 }
91
92 GST_INLINE_FUNC gpointer
93 gst_trash_stack_pop (GstTrashStack *stack)
94 {
95   GstTrashStackElement *head;
96
97   /* pop is a little more complicated as we need to avoid the so called ABA
98    * problem that arises when a pop and push of the same element happens
99    * right between when we read head->next and try to swing the new pointer
100    * into place. This is usually solved using a counter which makes it highly
101    * unlikely that we manage to grab the wrong head->next value.
102    */
103   __asm__ __volatile__ (
104     "  pushl %%ebx;             \n\t"
105     "  testl %%eax, %%eax;      \n\t"   /* if (head == NULL) return */
106     "  jz 20f;                  \n\t"
107     "10:                        \n\t"
108     "  movl (%%eax), %%ebx;     \n\t"   /* take value pointed to by head (head->next) */
109     "  movl %%edx, %%ecx;       \n\t"   /* take counter */
110     "  incl %%ecx;              \n\t"   /* and increment */
111     SMP_LOCK "cmpxchg8b %1;     \n\t"   /* if eax:edx == *stack, move ebx:ecx to *stack,
112                                          * else *stack is moved into eax:edx again... */
113     "  jnz 10b;                 \n\t"   /* ... and we retry */
114     "20:                        \n\t"
115     "  popl %%ebx               \n"
116       : "=a" (head)
117       :  "m" (*stack),
118          "a" (stack->head),
119          "d" (stack->count)
120       :  "ecx"
121   );
122
123   return head;
124 }
125
126 #else
127 #warning "using fallback trashstack implementation, performance may suffer"
128
129 /*
130  * generic implementation
131  */
132 GST_INLINE_FUNC void
133 gst_trash_stack_init (GstTrashStack *stack)
134 {
135   stack->head = NULL;
136   stack->lock = g_mutex_new();
137 }
138
139 GST_INLINE_FUNC void
140 gst_trash_stack_destroy (GstTrashStack *stack)
141 {
142   g_mutex_free (stack->lock);
143 }
144
145 GST_INLINE_FUNC void
146 gst_trash_stack_push (GstTrashStack *stack, gpointer mem)
147 {
148   GstTrashStackElement *elem = (GstTrashStackElement *) mem;
149
150   g_mutex_lock (stack->lock);
151   elem->next = stack->head;
152   stack->head = elem;
153   g_mutex_unlock (stack->lock);
154 }
155
156 GST_INLINE_FUNC gpointer
157 gst_trash_stack_pop (GstTrashStack *stack)
158 {
159   GstTrashStackElement *head;
160   
161   g_mutex_lock (stack->lock);
162   head = (GstTrashStackElement *) stack->head;
163   if (head)
164     stack->head = head->next;
165   g_mutex_unlock (stack->lock);
166
167   return head;
168 }
169
170 #endif
171
172 /*
173  * common functions
174  */
175 GST_INLINE_FUNC GstTrashStack*
176 gst_trash_stack_new (void)
177 {
178   GstTrashStack *stack;
179
180   stack = g_new (GstTrashStack, 1);
181   gst_trash_stack_init (stack);
182
183   return stack;
184 }
185
186 GST_INLINE_FUNC void
187 gst_trash_stack_free (GstTrashStack *stack)
188 {
189   gst_trash_stack_destroy (stack);
190   g_free (stack);
191 }
192
193 #endif /* defined (GST_CAN_INLINE) || defined (__GST_TRASH_STACK_C__)*/
194
195 G_END_DECLS
196
197 #endif /*  __GST_TRASH_STACK_H__ */