Start migrating from libglade to gtkbuilder.
[gmidimonitor.git] / memory_atomic.c
blobabda08e6e90bf21bf628f1387f4f637bccedc4a9
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*****************************************************************************
4 * Non-sleeping memory allocation
5 * This file is part of gmidimonitor
7 * Copyright (C) 2006,2007,2008,2011 Nedko Arnaudov <nedko@arnaudov.name>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License
13 * This program 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
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 *****************************************************************************/
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <gtk/gtk.h>
28 #include "memory_atomic.h"
29 #include "list.h"
30 //#define LOG_LEVEL LOG_LEVEL_DEBUG
31 #include "log.h"
33 struct rtsafe_memory_pool
35 size_t data_size;
36 size_t min_preallocated;
37 size_t max_preallocated;
39 struct list_head used;
40 unsigned int used_count;
42 struct list_head unused;
43 unsigned int unused_count;
46 #define RTSAFE_GROUPS_PREALLOCATE 1024
48 gboolean
49 rtsafe_memory_pool_create(
50 size_t data_size,
51 size_t min_preallocated,
52 size_t max_preallocated,
53 rtsafe_memory_pool_handle * pool_handle_ptr)
55 struct rtsafe_memory_pool * pool_ptr;
57 assert(min_preallocated <= max_preallocated);
59 pool_ptr = malloc(sizeof(struct rtsafe_memory_pool));
60 if (pool_ptr == NULL)
62 return FALSE;
65 pool_ptr->data_size = data_size;
66 pool_ptr->min_preallocated = min_preallocated;
67 pool_ptr->max_preallocated = max_preallocated;
69 INIT_LIST_HEAD(&pool_ptr->used);
70 pool_ptr->used_count = 0;
72 INIT_LIST_HEAD(&pool_ptr->unused);
73 pool_ptr->unused_count = 0;
75 rtsafe_memory_pool_sleepy((rtsafe_memory_pool_handle)pool_ptr);
76 *pool_handle_ptr = pool_ptr;
78 return TRUE;
81 #define pool_ptr ((struct rtsafe_memory_pool *)pool_handle)
83 void
84 rtsafe_memory_pool_destroy(
85 rtsafe_memory_pool_handle pool_handle)
87 struct list_head * node_ptr;
89 assert(pool_ptr->used_count == 0); /* called should deallocate all chunks prior releasing pool itself */
90 assert(list_empty(&pool_ptr->used));
92 while (pool_ptr->unused_count != 0)
94 assert(!list_empty(&pool_ptr->unused));
96 node_ptr = pool_ptr->unused.next;
98 list_del(node_ptr);
99 pool_ptr->unused_count--;
101 free(node_ptr);
104 assert(list_empty(&pool_ptr->unused));
106 free(pool_ptr);
109 /* adjust unused list size */
110 void
111 rtsafe_memory_pool_sleepy(
112 rtsafe_memory_pool_handle pool_handle)
114 struct list_head * node_ptr;
116 while (pool_ptr->unused_count < pool_ptr->min_preallocated)
118 node_ptr = malloc(sizeof(struct list_head) + pool_ptr->data_size);
119 if (node_ptr == NULL)
121 return;
124 list_add_tail(node_ptr, &pool_ptr->unused);
125 pool_ptr->unused_count++;
128 while (pool_ptr->unused_count > pool_ptr->max_preallocated)
130 assert(!list_empty(&pool_ptr->unused));
132 node_ptr = pool_ptr->unused.next;
134 list_del(node_ptr);
135 pool_ptr->unused_count--;
137 free(node_ptr);
141 /* find entry in unused list, fail if it is empty */
142 void *
143 rtsafe_memory_pool_allocate(
144 rtsafe_memory_pool_handle pool_handle)
146 struct list_head * node_ptr;
148 if (list_empty(&pool_ptr->unused))
150 return NULL;
153 node_ptr = pool_ptr->unused.next;
154 list_del(node_ptr);
155 pool_ptr->unused_count--;
156 pool_ptr->used_count++;
158 return (node_ptr + 1);
161 /* move from used to unused list */
162 void
163 rtsafe_memory_pool_deallocate(
164 rtsafe_memory_pool_handle pool_handle,
165 void * data)
167 list_add_tail((struct list_head *)data - 1, &pool_ptr->unused);
168 pool_ptr->used_count--;
169 pool_ptr->unused_count++;
172 void *
173 rtsafe_memory_pool_allocate_sleepy(
174 rtsafe_memory_pool_handle pool_handle)
176 void * data;
180 rtsafe_memory_pool_sleepy(pool_handle);
181 data = rtsafe_memory_pool_allocate(pool_handle);
183 while (data == NULL);
185 return data;
188 /* max alloc is DATA_MIN * (2 ^ POOLS_COUNT) - DATA_SUB */
189 #define DATA_MIN 1024
190 #define DATA_SUB 100 /* alloc slightly smaller chunks in hope to not allocating additional page for control data */
192 struct rtsafe_memory_pool_generic
194 size_t size;
195 rtsafe_memory_pool_handle pool;
198 struct rtsafe_memory
200 struct rtsafe_memory_pool_generic * pools;
201 size_t pools_count;
204 gboolean
205 rtsafe_memory_init(
206 size_t max_size,
207 size_t prealloc_min,
208 size_t prealloc_max,
209 rtsafe_memory_handle * handle_ptr)
211 size_t i;
212 size_t size;
213 struct rtsafe_memory * memory_ptr;
215 LOG_DEBUG("rtsafe_memory_init() called.");
217 memory_ptr = malloc(sizeof(struct rtsafe_memory));
218 if (memory_ptr == NULL)
220 goto fail;
223 size = DATA_MIN;
224 memory_ptr->pools_count = 1;
226 while ((size << memory_ptr->pools_count) < max_size + DATA_SUB)
228 memory_ptr->pools_count++;
230 if (memory_ptr->pools_count > sizeof(size_t) * 8)
232 assert(0); /* chances that caller really need such huge size are close to zero */
233 goto fail_free;
237 memory_ptr->pools = malloc(memory_ptr->pools_count * sizeof(struct rtsafe_memory_pool_generic));
238 if (memory_ptr->pools == NULL)
240 goto fail_free;
243 size = DATA_MIN;
245 for (i = 0 ; i < memory_ptr->pools_count ; i++)
247 memory_ptr->pools[i].size = size - DATA_SUB;
249 if (!rtsafe_memory_pool_create(
250 memory_ptr->pools[i].size,
251 prealloc_min,
252 prealloc_max,
253 &memory_ptr->pools[i].pool))
255 while (i > 0)
257 i--;
258 rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool);
261 goto fail_free_pools;
264 size = size << 1;
267 *handle_ptr = (rtsafe_memory_handle)memory_ptr;
269 return TRUE;
271 fail_free_pools:
272 free(memory_ptr->pools);
274 fail_free:
275 free(memory_ptr);
277 fail:
278 return FALSE;
281 #define memory_ptr ((struct rtsafe_memory *)handle_ptr)
282 void
283 rtsafe_memory_uninit(
284 rtsafe_memory_handle handle_ptr)
286 unsigned int i;
288 LOG_DEBUG("rtsafe_memory_uninit() called.");
290 for (i = 0 ; i < memory_ptr->pools_count ; i++)
292 LOG_DEBUG("Destroying pool for size %u", (unsigned int)memory_ptr->pools[i].size);
293 rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool);
296 free(memory_ptr->pools);
298 free(memory_ptr);
301 void *
302 rtsafe_memory_allocate(
303 rtsafe_memory_handle handle_ptr,
304 size_t size)
306 rtsafe_memory_pool_handle * data_ptr;
307 size_t i;
309 LOG_DEBUG("rtsafe_memory_allocate() called.");
311 /* pool handle is stored just before user data to ease deallocation */
312 size += sizeof(rtsafe_memory_pool_handle);
314 for (i = 0 ; i < memory_ptr->pools_count ; i++)
316 if (size <= memory_ptr->pools[i].size)
318 LOG_DEBUG("Using chunk with size %u.", (unsigned int)memory_ptr->pools[i].size);
319 data_ptr = rtsafe_memory_pool_allocate(memory_ptr->pools[i].pool);
320 if (data_ptr == NULL)
322 LOG_DEBUG("rtsafe_memory_pool_allocate() failed.");
323 return FALSE;
326 *data_ptr = memory_ptr->pools[i].pool;
328 LOG_DEBUG("rtsafe_memory_allocate() returning %p", (data_ptr + 1));
329 return (data_ptr + 1);
333 /* data size too big, increase POOLS_COUNT */
334 LOG_WARNING("Data size is too big");
335 return FALSE;
338 void
339 rtsafe_memory_sleepy(
340 rtsafe_memory_handle handle_ptr)
342 unsigned int i;
344 for (i = 0 ; i < memory_ptr->pools_count ; i++)
346 rtsafe_memory_pool_sleepy(memory_ptr->pools[i].pool);
350 void
351 rtsafe_memory_deallocate(
352 void * data)
354 LOG_DEBUG("rtsafe_memory_deallocate(%p) called.", data);
355 rtsafe_memory_pool_deallocate(
356 *((rtsafe_memory_pool_handle *)data -1),
357 (rtsafe_memory_pool_handle *)data - 1);