don't break docs build
[platform/upstream/gstreamer.git] / gst / gstmemchunk.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *               <2005> Wim Taymans <wim@fluendo.com>
4  *
5  * gstmemchunk.c: implementation of lockfree allocation of fixed
6  *                size memory chunks.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 /**
24  * SECTION:gstmemchunk
25  * @short_description: Atomic chunk allocator
26  * @see_also: #GstBuffer, #GstEvent, #GstData
27  *
28  * GstMemChunk is an atomic chunk allocator. It uses atomic operations to
29  * allocate fixed size memory regions and is therefore thread safe without the
30  * overhead of mutexes or other heavyweight locking mechanisms.
31  *
32  * The GstMemChunk is used to allocate critical resources for #GstBuffer and
33  * #GstEvent.
34  */
35 #include "gst_private.h"
36
37 #include <string.h>             /* memset */
38
39 #include "gstmemchunk.h"
40 #include "gsttrashstack.h"
41 #ifdef HAVE_VALGRIND
42 #include <sys/mman.h>
43 #include <valgrind/valgrind.h>
44 #endif
45
46 #define GST_MEM_CHUNK_AREA(chunk)       (((GstMemChunkElement*)(chunk))->area)
47 #define GST_MEM_CHUNK_DATA(chunk)       ((gpointer)(((GstMemChunkElement*)(chunk)) + 1))
48 #define GST_MEM_CHUNK_LINK(mem)         ((GstMemChunkElement*)((guint8*)(mem) - sizeof (GstMemChunkElement)))
49
50 typedef struct _GstMemChunkElement GstMemChunkElement;
51
52 struct _GstMemChunkElement
53 {
54   GstTrashStackElement elem;    /* make sure we can safely push it on the trashstack */
55   gpointer area;                /* pointer to data areas */
56 };
57
58 struct _GstMemChunk
59 {
60   GstTrashStack stack;
61
62   gchar *name;
63   gulong area_size;
64   gulong chunk_size;
65   gulong atom_size;
66   gboolean cleanup;
67 };
68
69 /*******************************************************
70  *         area size
71  * +-------------------------------------------------------+
72  *   chunk size
73  * +-----------------+
74  *
75  * !next!area|data... !next!area!data.... !next!area!data...
76  *  !                  ^ !                 ^ !
77  *  +------------------+ +-----------------+ +--------> NULL
78  *
79  */
80 static gboolean
81 populate (GstMemChunk * mem_chunk)
82 {
83   guint8 *area;
84   gint i;
85
86   if (mem_chunk->cleanup)
87     return FALSE;
88
89   /* FIXME: if we don't do this here and use g_malloc, valgrind crashes */
90 #if HAVE_VALGRIND
91   if (__gst_in_valgrind ()) {
92     /* copied from valgrind example */
93     area =
94         (guint8 *) mmap (0, mem_chunk->area_size,
95         PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);
96   } else
97 #endif
98   {
99     area = g_malloc0 (mem_chunk->area_size);
100   }
101
102   for (i = 0; i < mem_chunk->area_size; i += mem_chunk->chunk_size) {
103     GST_MEM_CHUNK_AREA (area + i) = area;
104     gst_trash_stack_push (&mem_chunk->stack, area + i);
105   }
106
107   return TRUE;
108 }
109
110 /**
111  * gst_mem_chunk_new:
112  * @name: the name of the chunk
113  * @atom_size: the size of the allocated atoms
114  * @area_size: the initial size of the memory area
115  * @type: the allocation strategy to use
116  *
117  * Creates a new memchunk that will allocate atom_sized memchunks.
118  * The initial area is set to area_size and will grow automatically 
119  * when it is too small (with a small overhead when that happens)
120  *
121  * Returns: a new #GstMemChunk
122  *
123  * MT safe.
124  */
125 GstMemChunk *
126 gst_mem_chunk_new (gchar * name, gint atom_size, gulong area_size, gint type)
127 {
128   GstMemChunk *mem_chunk;
129
130   g_return_val_if_fail (atom_size > 0, NULL);
131   g_return_val_if_fail (area_size >= atom_size, NULL);
132
133   mem_chunk = g_malloc (sizeof (GstMemChunk));
134
135   mem_chunk->chunk_size = atom_size + sizeof (GstMemChunkElement);
136   area_size = (area_size / atom_size) * mem_chunk->chunk_size;
137
138   mem_chunk->name = g_strdup (name);
139   mem_chunk->atom_size = atom_size;
140   mem_chunk->area_size = area_size;
141   mem_chunk->cleanup = FALSE;
142   gst_trash_stack_init (&mem_chunk->stack);
143
144   populate (mem_chunk);
145
146   return mem_chunk;
147 }
148
149 static gboolean
150 free_area (gpointer key, gpointer value, gpointer user_data)
151 {
152 #if HAVE_VALGRIND
153   GstMemChunk *chunk = (GstMemChunk *) user_data;
154
155   if (__gst_in_valgrind ()) {
156     /* copied from valgrind example */
157     munmap (key, chunk->area_size);
158   } else
159 #endif
160   {
161     g_free (key);
162   }
163
164   return TRUE;
165 }
166
167 /**
168  * gst_mem_chunk_destroy:
169  * @mem_chunk: the GstMemChunk to destroy
170  *
171  * Free the memory allocated by the memchunk. This function
172  * is not Threadsafe as it does not wait for all outstanding
173  * allocations to be freed.
174  */
175 void
176 gst_mem_chunk_destroy (GstMemChunk * mem_chunk)
177 {
178   GHashTable *elements = g_hash_table_new (NULL, NULL);
179   gpointer data;
180
181   mem_chunk->cleanup = TRUE;
182
183   data = gst_mem_chunk_alloc (mem_chunk);
184   while (data) {
185     GstMemChunkElement *elem = GST_MEM_CHUNK_LINK (data);
186
187     g_hash_table_insert (elements, GST_MEM_CHUNK_AREA (elem), NULL);
188
189     data = gst_mem_chunk_alloc (mem_chunk);
190   }
191   g_hash_table_foreach_remove (elements, free_area, mem_chunk);
192
193   g_hash_table_destroy (elements);
194   g_free (mem_chunk->name);
195   g_free (mem_chunk);
196 }
197
198 /**
199  * gst_mem_chunk_alloc:
200  * @mem_chunk: the mem chunk to use
201  *
202  * Allocate a new memory region from the chunk. The size
203  * of the allocated memory was specified when the memchunk
204  * was created.
205  *
206  * Returns: a pointer to the allocated memory region.
207  *
208  * MT safe.
209  */
210 gpointer
211 gst_mem_chunk_alloc (GstMemChunk * mem_chunk)
212 {
213   GstMemChunkElement *chunk;
214
215   g_return_val_if_fail (mem_chunk != NULL, NULL);
216
217 again:
218   chunk = gst_trash_stack_pop (&mem_chunk->stack);
219   /* chunk is empty, try to refill */
220   if (G_UNLIKELY (!chunk)) {
221     if (G_LIKELY (populate (mem_chunk))) {
222       goto again;
223     } else {
224       /* this happens when we are in cleanup mode and we
225        * allocate all remaining chunks for cleanup */
226       return NULL;
227     }
228   }
229 #ifdef HAVE_VALGRIND
230   if (G_UNLIKELY (__gst_in_valgrind ())) {
231     VALGRIND_MALLOCLIKE_BLOCK (GST_MEM_CHUNK_DATA (chunk), mem_chunk->atom_size,
232         0, 0);
233   }
234 #endif
235   return GST_MEM_CHUNK_DATA (chunk);
236 }
237
238 /**
239  * gst_mem_chunk_alloc0:
240  * @mem_chunk: the mem chunk to use
241  *
242  * Allocate a new memory region from the chunk. The size
243  * of the allocated memory was specified when the memchunk
244  * was created. The memory will be set to all zeroes.
245  *
246  * Returns: a pointer to the allocated memory region.
247  *
248  * MT safe.
249  */
250 gpointer
251 gst_mem_chunk_alloc0 (GstMemChunk * mem_chunk)
252 {
253   gpointer mem = gst_mem_chunk_alloc (mem_chunk);
254
255   if (G_LIKELY (mem))
256     memset (mem, 0, mem_chunk->atom_size);
257
258   return mem;
259 }
260
261 /**
262  * gst_mem_chunk_free:
263  * @mem_chunk: the mem chunk to use
264  * @mem: the memory region to hand back to the chunk
265  *
266  * Free the memeory region allocated from the chunk.
267  *
268  * MT safe.
269  */
270 void
271 gst_mem_chunk_free (GstMemChunk * mem_chunk, gpointer mem)
272 {
273   GstMemChunkElement *chunk;
274
275   g_return_if_fail (mem_chunk != NULL);
276   g_return_if_fail (mem != NULL);
277
278   chunk = GST_MEM_CHUNK_LINK (mem);
279
280 #ifdef HAVE_VALGRIND
281   if (G_UNLIKELY (__gst_in_valgrind ())) {
282     VALGRIND_FREELIKE_BLOCK (mem, 0);
283   }
284 #endif
285   gst_trash_stack_push (&mem_chunk->stack, chunk);
286 }