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