* eina: add assert to ease tracking down efl misuse with threads.
[profile/ivi/eina.git] / src / modules / mp / chained_pool / eina_chained_mempool.c
1 /* EINA - EFL data type library
2  * Copyright (C) 2008-2010 Cedric BAIL, Vincent Torri
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #ifdef EFL_HAVE_POSIX_THREADS
27 #include <pthread.h>
28 #endif
29
30 #ifdef EFL_HAVE_WIN32_THREADS
31 # define WIN32_LEAN_AND_MEAN
32 # include <windows.h>
33 # undef WIN32_LEAN_AND_MEAN
34 #endif
35
36 #include "eina_inlist.h"
37 #include "eina_error.h"
38 #include "eina_module.h"
39 #include "eina_mempool.h"
40 #include "eina_trash.h"
41
42 #include "eina_private.h"
43
44 #ifdef DEBUG
45 #include "eina_log.h"
46
47 static int _eina_mempool_log_dom = -1;
48
49 #ifdef INF
50 #undef INF
51 #endif
52 #define INF(...) EINA_LOG_DOM_INFO(_eina_mempool_log_dom, __VA_ARGS__)
53 #endif
54
55 #ifdef EFL_DEBUG_THREADS
56 #include <assert.h>
57 #endif
58
59 typedef struct _Chained_Mempool Chained_Mempool;
60 struct _Chained_Mempool
61 {
62    Eina_Inlist *first;
63    const char *name;
64    int item_alloc;
65    int pool_size;
66    int alloc_size;
67    int group_size;
68    int usage;
69 #ifdef EFL_HAVE_THREADS
70 #ifdef EFL_DEBUG_THREADS
71    pthread_t self;
72 #endif
73 # ifdef EFL_HAVE_POSIX_THREADS
74    pthread_mutex_t mutex;
75 # else
76    HANDLE mutex;
77 # endif
78 #endif
79 };
80
81 typedef struct _Chained_Pool Chained_Pool;
82 struct _Chained_Pool
83 {
84    EINA_INLIST;
85    Eina_Trash *base;
86    int usage;
87
88    unsigned char *last;
89    unsigned char *limit;
90 };
91
92 static inline Chained_Pool *
93 _eina_chained_mp_pool_new(Chained_Mempool *pool)
94 {
95    Chained_Pool *p;
96    unsigned char *ptr;
97
98    eina_error_set(0);
99    p = malloc(pool->alloc_size);
100    if (!p)
101      {
102         eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
103         return NULL;
104      }
105
106    ptr = (unsigned char *)p + eina_mempool_alignof(sizeof(Chained_Pool));
107    p->usage = 0;
108    p->base = NULL;
109
110    p->last = ptr;
111    p->limit = ptr + pool->item_alloc * pool->pool_size;
112    return p;
113 }
114
115 static inline void
116 _eina_chained_mp_pool_free(Chained_Pool *p)
117 {
118    free(p);
119 }
120
121 static void *
122 eina_chained_mempool_malloc(void *data, __UNUSED__ unsigned int size)
123 {
124    Chained_Mempool *pool = data;
125    Chained_Pool *p = NULL;
126    void *mem;
127
128 #ifdef EFL_HAVE_THREADS
129    if (_threads_activated)
130      {
131 # ifdef EFL_HAVE_POSIX_THREADS
132         pthread_mutex_lock(&pool->mutex);
133 # else
134         WaitForSingleObject(pool->mutex, INFINITE);
135 # endif
136      }
137 #ifdef EFL_DEBUG_THREADS
138    else
139      assert(pool->self == pthread_self());
140 #endif
141 #endif
142
143    // look 4 pool from 2nd bucket on
144    EINA_INLIST_FOREACH(pool->first, p)
145    {
146       // base is not NULL - has a free slot
147       if (p->base || p->last)
148         {
149            pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p));
150            break;
151         }
152    }
153
154    // we have reached the end of the list - no free pools
155    if (!p)
156      {
157         p = _eina_chained_mp_pool_new(pool);
158         if (!p)
159           {
160 #ifdef EFL_HAVE_PTHREAD
161              if (_threads_activated)
162                {
163 # ifdef EFL_HAVE_POSIX_THREADS
164                   pthread_mutex_unlock(&pool->mutex);
165 # else
166                   ReleaseMutex(pool->mutex);
167 # endif
168                }
169 #endif
170              return NULL;
171           }
172
173         pool->first = eina_inlist_prepend(pool->first, EINA_INLIST_GET(p));
174      }
175
176    if (p->last)
177      {
178         mem = p->last;
179         p->last += pool->item_alloc;
180         if (p->last >= p->limit)
181           p->last = NULL;
182      }
183    else
184      // Request a free pointer
185      mem = eina_trash_pop(&p->base);
186
187    // move to end - it just filled up
188    if (!p->base && !p->last)
189       pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p));
190
191    p->usage++;
192    pool->usage++;
193
194 #ifdef EFL_HAVE_THREADS
195    if (_threads_activated)
196      {
197 # ifdef EFL_HAVE_POSIX_THREADS
198         pthread_mutex_unlock(&pool->mutex);
199 # else
200         ReleaseMutex(pool->mutex);
201 # endif
202      }
203 #endif
204
205    return mem;
206 }
207
208 static void
209 eina_chained_mempool_free(void *data, void *ptr)
210 {
211    Chained_Mempool *pool = data;
212    Chained_Pool *p;
213    void *pmem;
214    int psize;
215
216    psize = pool->group_size;
217    // look 4 pool
218
219 #ifdef EFL_HAVE_THREADS
220    if (_threads_activated)
221      {
222 # ifdef EFL_HAVE_POSIX_THREADS
223         pthread_mutex_lock(&pool->mutex);
224 # else
225         WaitForSingleObject(pool->mutex, INFINITE);
226 # endif
227      }
228 #ifdef EFL_DEBUG_THREADS
229    else
230      assert(pool->self == pthread_self());
231 #endif
232 #endif
233
234    EINA_INLIST_FOREACH(pool->first, p)
235    {
236       // Could the pointer be inside that pool
237       if ((unsigned char*) ptr < p->limit)
238         {
239            // pool mem base
240            pmem = (void *)(((unsigned char *)p) + sizeof(Chained_Pool));
241            // is it in pool mem?
242            if (ptr >= pmem)
243              {
244                 // freed node points to prev free node
245                 eina_trash_push(&p->base, ptr);
246                 // next free node is now the one we freed
247                 p->usage--;
248                 pool->usage--;
249                 if (p->usage == 0)
250                   {
251                      // free bucket
252                      pool->first = eina_inlist_remove(pool->first, EINA_INLIST_GET(p));
253                      _eina_chained_mp_pool_free(p);
254                   }
255                 else
256                   // move to front
257                   pool->first = eina_inlist_promote(pool->first, EINA_INLIST_GET(p));
258
259                 break;
260              }
261         }
262    }
263
264 #ifdef EFL_HAVE_THREADS
265    if (_threads_activated)
266      {
267 # ifdef EFL_HAVE_POSIX_THREADS
268         pthread_mutex_unlock(&pool->mutex);
269 # else
270         ReleaseMutex(pool->mutex);
271 # endif
272      }
273 #endif
274 }
275
276 static void *
277 eina_chained_mempool_realloc(__UNUSED__ void *data,
278                              __UNUSED__ void *element,
279                              __UNUSED__ unsigned int size)
280 {
281    return NULL;
282 }
283
284 static void *
285 eina_chained_mempool_init(const char *context,
286                           __UNUSED__ const char *option,
287                           va_list args)
288 {
289    Chained_Mempool *mp;
290    int item_size;
291    size_t length;
292
293    length = context ? strlen(context) + 1 : 0;
294
295    mp = calloc(1, sizeof(Chained_Mempool) + length);
296    if (!mp)
297       return NULL;
298
299    item_size = va_arg(args, int);
300    mp->pool_size = va_arg(args, int);
301
302    if (length)
303      {
304         mp->name = (const char *)(mp + 1);
305         memcpy((char *)mp->name, context, length);
306      }
307
308    mp->item_alloc = eina_mempool_alignof(item_size);
309    mp->group_size = mp->item_alloc * mp->pool_size;
310    mp->alloc_size = mp->group_size + eina_mempool_alignof(sizeof(Chained_Pool));
311 #ifdef EFL_DEBUG_THREADS
312    mp->self = pthread_self();
313 #endif
314
315 #ifdef EFL_HAVE_THREADS
316 # ifdef EFL_HAVE_POSIX_THREADS
317    pthread_mutex_init(&mp->mutex, NULL);
318 # else
319    mp->mutex = CreateMutex(NULL, FALSE, NULL);
320 # endif
321 #endif
322
323    return mp;
324 }
325
326 static void
327 eina_chained_mempool_shutdown(void *data)
328 {
329    Chained_Mempool *mp;
330
331    mp = (Chained_Mempool *)data;
332
333    while (mp->first)
334      {
335         Chained_Pool *p = (Chained_Pool *)mp->first;
336
337 #ifdef DEBUG
338         if (p->usage > 0)
339            INF("Bad news we are destroying not an empty mempool [%s]\n",
340                mp->name);
341
342 #endif
343
344         mp->first = eina_inlist_remove(mp->first, mp->first);
345         _eina_chained_mp_pool_free(p);
346      }
347
348 #ifdef EFL_HAVE_THREADS
349 #ifdef EFL_DEBUG_THREADS
350    assert(mp->self == pthread_self());
351 #endif
352 # ifdef EFL_HAVE_POSIX_THREADS
353    pthread_mutex_destroy(&mp->mutex);
354 # else
355    CloseHandle(mp->mutex);
356 # endif
357 #endif
358
359    free(mp);
360 }
361
362 static Eina_Mempool_Backend _eina_chained_mp_backend = {
363    "chained_mempool",
364    &eina_chained_mempool_init,
365    &eina_chained_mempool_free,
366    &eina_chained_mempool_malloc,
367    &eina_chained_mempool_realloc,
368    NULL,
369    NULL,
370    &eina_chained_mempool_shutdown
371 };
372
373 Eina_Bool chained_init(void)
374 {
375 #ifdef DEBUG
376    _eina_mempool_log_dom = eina_log_domain_register("eina_mempool",
377                                                     EINA_LOG_COLOR_DEFAULT);
378    if (_eina_mempool_log_dom < 0)
379      {
380         EINA_LOG_ERR("Could not register log domain: eina_mempool");
381         return EINA_FALSE;
382      }
383
384 #endif
385    return eina_mempool_register(&_eina_chained_mp_backend);
386 }
387
388 void chained_shutdown(void)
389 {
390    eina_mempool_unregister(&_eina_chained_mp_backend);
391 #ifdef DEBUG
392    eina_log_domain_unregister(_eina_mempool_log_dom);
393    _eina_mempool_log_dom = -1;
394 #endif
395 }
396
397 #ifndef EINA_STATIC_BUILD_CHAINED_POOL
398
399 EINA_MODULE_INIT(chained_init);
400 EINA_MODULE_SHUTDOWN(chained_shutdown);
401
402 #endif /* ! EINA_STATIC_BUILD_CHAINED_POOL */