pkgconfig/: New files.
[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 #include "gst_private.h"
24
25 #include <string.h>             /* memset */
26
27 #include "gstmemchunk.h"
28 #include "gsttrashstack.h"
29 #ifdef HAVE_VALGRIND
30 #include <sys/mman.h>
31 #include <valgrind/valgrind.h>
32 #endif
33
34 #define GST_MEM_CHUNK_AREA(chunk)       (((GstMemChunkElement*)(chunk))->area)
35 #define GST_MEM_CHUNK_DATA(chunk)       ((gpointer)(((GstMemChunkElement*)(chunk)) + 1))
36 #define GST_MEM_CHUNK_LINK(mem)         ((GstMemChunkElement*)((guint8*)(mem) - sizeof (GstMemChunkElement)))
37
38 typedef struct _GstMemChunkElement GstMemChunkElement;
39
40 struct _GstMemChunkElement
41 {
42   GstTrashStackElement elem;    /* make sure we can safely push it on the trashstack */
43   gpointer area;                /* pointer to data areas */
44 };
45
46 struct _GstMemChunk
47 {
48   GstTrashStack stack;
49
50   gchar *name;
51   gulong area_size;
52   gulong chunk_size;
53   gulong atom_size;
54   gboolean cleanup;
55 };
56
57 /*******************************************************
58  *         area size
59  * +-------------------------------------------------------+
60  *   chunk size
61  * +-----------------+
62  *
63  * !next!area|data... !next!area!data.... !next!area!data...
64  *  !                  ^ !                 ^ !
65  *  +------------------+ +-----------------+ +--------> NULL
66  *
67  */
68 static gboolean
69 populate (GstMemChunk * mem_chunk)
70 {
71   guint8 *area;
72   gint i;
73
74   if (mem_chunk->cleanup)
75     return FALSE;
76
77   /* FIXME: if we don't do this here and use g_malloc, valgrind crashes */
78 #if HAVE_VALGRIND
79   if (__gst_in_valgrind ()) {
80     /* copied from valgrind example */
81     area =
82         (guint8 *) mmap (0, mem_chunk->area_size,
83         PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);
84   } else
85 #endif
86   {
87     area = g_malloc0 (mem_chunk->area_size);
88   }
89
90   for (i = 0; i < mem_chunk->area_size; i += mem_chunk->chunk_size) {
91     GST_MEM_CHUNK_AREA (area + i) = area;
92     gst_trash_stack_push (&mem_chunk->stack, area + i);
93   }
94
95   return TRUE;
96 }
97
98 /**
99  * gst_mem_chunk_new:
100  * @name: the name of the chunk
101  * @atom_size: the size of the allocated atoms
102  * @area_size: the initial size of the memory area
103  * @type: the allocation strategy to use
104  *
105  * Creates a new memchunk that will allocate atom_sized memchunks.
106  * The initial area is set to area_size and will grow automatically 
107  * when it is too small (with a small overhead when that happens)
108  *
109  * Returns: a new #GstMemChunk
110  *
111  * MT safe.
112  */
113 GstMemChunk *
114 gst_mem_chunk_new (gchar * name, gint atom_size, gulong area_size, gint type)
115 {
116   GstMemChunk *mem_chunk;
117
118   g_return_val_if_fail (atom_size > 0, NULL);
119   g_return_val_if_fail (area_size >= atom_size, NULL);
120
121   mem_chunk = g_malloc (sizeof (GstMemChunk));
122
123   mem_chunk->chunk_size = atom_size + sizeof (GstMemChunkElement);
124   area_size = (area_size / atom_size) * mem_chunk->chunk_size;
125
126   mem_chunk->name = g_strdup (name);
127   mem_chunk->atom_size = atom_size;
128   mem_chunk->area_size = area_size;
129   mem_chunk->cleanup = FALSE;
130   gst_trash_stack_init (&mem_chunk->stack);
131
132   populate (mem_chunk);
133
134   return mem_chunk;
135 }
136
137 static gboolean
138 free_area (gpointer key, gpointer value, gpointer user_data)
139 {
140 #if HAVE_VALGRIND
141   GstMemChunk *chunk = (GstMemChunk *) user_data;
142
143   if (__gst_in_valgrind ()) {
144     /* copied from valgrind example */
145     munmap (key, chunk->area_size);
146   } else
147 #endif
148   {
149     g_free (key);
150   }
151
152   return TRUE;
153 }
154
155 /**
156  * gst_mem_chunk_destroy:
157  * @mem_chunk: the GstMemChunk to destroy
158  *
159  * Free the memory allocated by the memchunk. This function
160  * is not Threadsafe as it does not wait for all outstanding
161  * allocations to be freed.
162  */
163 void
164 gst_mem_chunk_destroy (GstMemChunk * mem_chunk)
165 {
166   GHashTable *elements = g_hash_table_new (NULL, NULL);
167   gpointer data;
168
169   mem_chunk->cleanup = TRUE;
170
171   data = gst_mem_chunk_alloc (mem_chunk);
172   while (data) {
173     GstMemChunkElement *elem = GST_MEM_CHUNK_LINK (data);
174
175     g_hash_table_insert (elements, GST_MEM_CHUNK_AREA (elem), NULL);
176
177     data = gst_mem_chunk_alloc (mem_chunk);
178   }
179   g_hash_table_foreach_remove (elements, free_area, mem_chunk);
180
181   g_hash_table_destroy (elements);
182   g_free (mem_chunk->name);
183   g_free (mem_chunk);
184 }
185
186 /**
187  * gst_mem_chunk_alloc:
188  * @mem_chunk: the mem chunk to use
189  *
190  * Allocate a new memory region from the chunk. The size
191  * of the allocated memory was specified when the memchunk
192  * was created.
193  *
194  * Returns: a pointer to the allocated memory region.
195  *
196  * MT safe.
197  */
198 gpointer
199 gst_mem_chunk_alloc (GstMemChunk * mem_chunk)
200 {
201   GstMemChunkElement *chunk;
202
203   g_return_val_if_fail (mem_chunk != NULL, NULL);
204
205 again:
206   chunk = gst_trash_stack_pop (&mem_chunk->stack);
207   /* chunk is empty, try to refill */
208   if (G_UNLIKELY (!chunk)) {
209     if (G_LIKELY (populate (mem_chunk))) {
210       goto again;
211     } else {
212       /* this happens when we are in cleanup mode and we
213        * allocate all remaining chunks for cleanup */
214       return NULL;
215     }
216   }
217 #ifdef HAVE_VALGRIND
218   if (G_UNLIKELY (__gst_in_valgrind ())) {
219     VALGRIND_MALLOCLIKE_BLOCK (GST_MEM_CHUNK_DATA (chunk), mem_chunk->atom_size,
220         0, 0);
221   }
222 #endif
223   return GST_MEM_CHUNK_DATA (chunk);
224 }
225
226 /**
227  * gst_mem_chunk_alloc0:
228  * @mem_chunk: the mem chunk to use
229  *
230  * Allocate a new memory region from the chunk. The size
231  * of the allocated memory was specified when the memchunk
232  * was created. The memory will be set to all zeroes.
233  *
234  * Returns: a pointer to the allocated memory region.
235  *
236  * MT safe.
237  */
238 gpointer
239 gst_mem_chunk_alloc0 (GstMemChunk * mem_chunk)
240 {
241   gpointer mem = gst_mem_chunk_alloc (mem_chunk);
242
243   if (G_LIKELY (mem))
244     memset (mem, 0, mem_chunk->atom_size);
245
246   return mem;
247 }
248
249 /**
250  * gst_mem_chunk_free:
251  * @mem_chunk: the mem chunk to use
252  * @mem: the memory region to hand back to the chunk
253  *
254  * Free the memeory region allocated from the chunk.
255  *
256  * MT safe.
257  */
258 void
259 gst_mem_chunk_free (GstMemChunk * mem_chunk, gpointer mem)
260 {
261   GstMemChunkElement *chunk;
262
263   g_return_if_fail (mem_chunk != NULL);
264   g_return_if_fail (mem != NULL);
265
266   chunk = GST_MEM_CHUNK_LINK (mem);
267
268 #ifdef HAVE_VALGRIND
269   if (G_UNLIKELY (__gst_in_valgrind ())) {
270     VALGRIND_FREELIKE_BLOCK (mem, 0);
271   }
272 #endif
273   gst_trash_stack_push (&mem_chunk->stack, chunk);
274 }