1 /* EINA - EFL data type library
2 * Copyright (C) 2011 Cedric Bail
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.
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.
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/>.
25 #include "eina_private.h"
26 #include "eina_inlist.h"
27 #include "eina_rbtree.h"
28 #include "eina_mempool.h"
29 #include "eina_trash.h"
31 #include "eina_stringshare.h"
32 #include "eina_lock.h"
34 #include "eina_object.h"
36 /*============================================================================*
38 *============================================================================*/
44 /* If we are on a 64bits computer user bigger generation and ID */
45 /* FIXME: make it GCC independent */
46 /* FIXME: maybe having 2^32 objects doesn't make sense and 2^24 are enough
47 so instead of increasing the object count, we could just add a magic
48 to first check if the pointer is valid at all (and maybe use a pointer
49 that will always trigger a segv if we try to use it directly).
52 typedef unsigned long Eina_Object_ID;
53 typedef unsigned short Eina_Object_Generation;
54 # define EINA_GEN_OFFSET 48
55 # define EINA_ID_STR "%lu"
57 typedef unsigned __int64 Eina_Object_ID;
58 typedef unsigned short Eina_Object_Generation;
59 # define EINA_GEN_OFFSET 48
60 # define EINA_ID_STR "%I64u"
62 typedef unsigned int Eina_Object_ID;
63 typedef unsigned char Eina_Object_Generation;
64 # define EINA_GEN_OFFSET 24
65 # define EINA_ID_STR "%u"
68 typedef struct _Eina_Class_Range Eina_Class_Range;
69 typedef struct _Eina_Object_Item Eina_Object_Item;
70 typedef struct _Eina_Range Eina_Range;
71 typedef struct _Eina_Class_Top Eina_Class_Top;
81 struct _Eina_Object_Item
84 Eina_Class_Range *range;
86 Eina_Object_Item *parent;
92 struct _Eina_Class_Range
100 unsigned int empty_count;
105 Eina_Object_Item **pointer_array;
106 Eina_Object_Generation generation_array[1];
109 struct _Eina_Class_Top
111 Eina_Class *top_parent;
113 Eina_Inlist *available;
115 unsigned int upper_limit;
129 Eina_Class_Callback constructor;
130 Eina_Class_Callback destructor;
133 Eina_Inlist *allocated_range;
135 Eina_Mempool *mempool;
136 unsigned int class_size;
137 unsigned int object_size;
138 unsigned int pool_size;
140 Eina_Bool repack_needed : 1;
142 #ifdef EINA_HAVE_DEBUG_THREADS
150 static const char EINA_MAGIC_CLASS_STR[] = "Eina Class";
152 static Eina_Mempool *_eina_class_mp = NULL;
153 static Eina_Mempool *_eina_top_mp = NULL;
154 static Eina_Mempool *_eina_range_mp = NULL;
155 static int _eina_object_log_dom = -1;
156 static unsigned int _eina_object_item_size = 0;
161 #define ERR(...) EINA_LOG_DOM_ERR(_eina_object_log_dom, __VA_ARGS__)
166 #define DBG(...) EINA_LOG_DOM_DBG(_eina_object_log_dom, __VA_ARGS__)
168 #define EINA_MAGIC_CHECK_CLASS(d, ...) \
170 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_CLASS)) \
172 EINA_MAGIC_FAIL(d, EINA_MAGIC_CLASS); \
173 return __VA_ARGS__; \
178 _eina_rbtree_cmp_range(const Eina_Rbtree *node, const void *key,
179 __UNUSED__ int length, __UNUSED__ void *data)
181 Eina_Class_Range *range;
184 range = EINA_RBTREE_CONTAINER_GET(node, Eina_Class_Range);
185 id = (Eina_Object_ID) key;
187 if (id < range->start) return -1;
188 else if (id >= range->end) return 1;
192 static Eina_Rbtree_Direction
193 _eina_class_direction_range(const Eina_Rbtree *left,
194 const Eina_Rbtree *right,
195 __UNUSED__ void *data)
197 Eina_Class_Range *rl;
198 Eina_Class_Range *rr;
200 rl = EINA_RBTREE_CONTAINER_GET(left, Eina_Class_Range);
201 rr = EINA_RBTREE_CONTAINER_GET(right, Eina_Class_Range);
203 if (rl->start < rr->start) return EINA_RBTREE_LEFT;
204 return EINA_RBTREE_RIGHT;
207 /* really destroying a range and handling that case is a complex
208 problem to solve. Not handling it right now, exposing myself to
211 _eina_range_cleanup(Eina_Class_Range *range)
218 _eina_object_constructor_call(Eina_Class *class, void *object)
220 if (class->parent) _eina_object_constructor_call(class->parent, object);
222 if (class->constructor) class->constructor(class, object, class->data);
226 _eina_object_destructor_call(Eina_Class *class, void *object)
228 if (class->destructor) class->destructor(class, object, class->data);
230 if (class->parent) _eina_object_destructor_call(class->parent, object);
234 _eina_object_get(Eina_Object_Item *item)
236 Eina_Object_Generation gen;
239 if (!item) return NULL;
241 gen = item->range->generation_array[item->index];
243 id = ((Eina_Object_ID) gen << EINA_GEN_OFFSET) + item->range->start + item->index;
245 return (Eina_Object *) id;
248 static Eina_Object_Item *
249 _eina_object_find_item(Eina_Class *class, Eina_Object *object)
251 Eina_Class_Range *matched;
252 Eina_Object_Item *item;
255 Eina_Object_Generation generation;
259 id = (Eina_Object_ID) object;
260 idx = id & (((Eina_Object_ID) 1 << EINA_GEN_OFFSET) - 1);
261 generation = id & !(((Eina_Object_ID) 1 << EINA_GEN_OFFSET) - 1);
263 /* Try to find the ID */
264 match = eina_rbtree_inline_lookup(class->top->range,
265 (void*) idx, sizeof (Eina_Object_ID),
266 _eina_rbtree_cmp_range, NULL);
267 /* ID not found, invalid pointer ! */
270 ERR("%p: ID ["EINA_ID_STR"] not found in class hiearchy of [%s].",
271 object, idx, class->name);
274 matched = EINA_RBTREE_CONTAINER_GET(match, Eina_Class_Range);
276 /* generation mismatch, invalid pointer ! */
277 if (generation != matched->generation_array[idx - matched->start])
279 ERR("%p: generation mismatch [%i] vs [%i].",
280 object, generation, matched->generation_array[idx - matched->start]);
284 /* does it belong to the right class ? */
285 for (search = matched->type; search && search != class; search = search->parent)
288 /* class match request or invalid pointer ! */
291 ERR("%p: from class [%s] is not in the hierarchie of [%s].",
292 object, matched->type->name, class->name);
296 /* retrieve pointer */
297 item = matched->pointer_array[id - matched->start];
298 /* allocated or invalid pointer ? */
301 ERR("%p: ID is not allocated.", object);
305 /* be aware, that because we use eina_trash to store empty pointer after first use,
306 pointer could be != NULL and still be empty. Need another pool of data somewhere
307 to store the state of the allocation... Is it needed ?
314 _eina_object_item_del(Eina_Object_Item *item)
318 class = item->range->type;
319 _eina_object_destructor_call(class,
320 (unsigned char*) item + _eina_object_item_size);
322 eina_trash_push(&item->range->empty, item->range->pointer_array + item->index);
323 item->range->generation_array[item->index]++;
324 item->range->empty_count++;
326 if (item->range->empty_count == item->range->end - item->range->start)
327 _eina_range_cleanup(item->range);
331 item->parent->link = eina_inlist_remove(item->parent->link, EINA_INLIST_GET(item));
336 Eina_Object_Item *child;
338 child = EINA_INLIST_CONTAINER_GET(item->link, Eina_Object_Item);
339 _eina_object_item_del(child);
342 eina_mempool_free(class->mempool, item);
345 static Eina_Class_Range *
346 _eina_class_empty_range_get(Eina_Inlist *list)
350 Eina_Class_Range *range;
352 range = EINA_INLIST_CONTAINER_GET(list, Eina_Class_Range);
353 if (range->empty_count > 0)
362 static Eina_Class_Range *
363 _eina_class_range_add(Eina_Class *class)
365 Eina_Class_Range *range;
367 Eina_Range *av = NULL;
369 range = malloc(sizeof (Eina_Class_Range)
370 + sizeof (Eina_Object_Item*) * class->pool_size
371 + sizeof (Eina_Object_Generation) * (class->pool_size - 1));
372 if (!range) return NULL;
374 tmp = (void*) (range + 1);
375 tmp += sizeof (Eina_Object_Generation) * (class->pool_size - 1);
377 range->pointer_array = (Eina_Object_Item**) tmp;
379 /* no need to fix generation to a specific value as random value should be just fine */
380 memset(range->pointer_array, 0, sizeof (Eina_Object_Item*) * class->pool_size);
386 /* and now find an empty block */
387 EINA_INLIST_FOREACH(class->top->available, av)
388 if ((av->end - av->start) == class->pool_size)
393 range->start = av->start;
394 range->end = av->end;
396 class->top->available = eina_inlist_remove(class->top->available,
397 EINA_INLIST_GET(av));
398 eina_mempool_free(_eina_range_mp, av);
402 range->start = class->top->upper_limit;
403 range->end = range->start + class->pool_size;
404 class->top->upper_limit = range->end;
411 _eina_class_range_del(Eina_Class_Range *range)
416 top = range->type->top;
418 keep = eina_mempool_malloc(_eina_range_mp, sizeof (Eina_Range));
421 ERR("Not enougth memory to keep track of allocated range.");
422 goto delete_class_range;
425 keep->start = range->start;
426 keep->end = range->end;
428 top->available = eina_inlist_prepend(top->available,
429 EINA_INLIST_GET(keep));
432 top->range = eina_rbtree_inline_remove(top->range, EINA_RBTREE_GET(range),
433 _eina_class_direction_range, NULL);
434 range->type->allocated_range = eina_inlist_remove(range->type->allocated_range,
435 EINA_INLIST_GET(range));
440 _eina_class_range_repack(void *dst, void *src, void *data)
442 Eina_Object_Item *di = dst;
443 Eina_Object_Item *si = src;
444 Eina_Class *class = data;
448 si->range->pointer_array[si->index] = di;
450 /* FIXME: We could just lock the right Eina_Class_Range
451 here instead of locking all the Class */
459 eina_object_init(void)
461 _eina_object_log_dom = eina_log_domain_register("eina_object",
462 EINA_LOG_COLOR_DEFAULT);
463 if (_eina_object_log_dom < 0)
465 EINA_LOG_ERR("Could not register log domain: eina_list");
469 _eina_class_mp = eina_mempool_add("chained_mempool", "class",
470 NULL, sizeof (Eina_Class), 16);
473 ERR("ERROR: Mempool for Eina_Class cannot be allocated in object init.");
477 _eina_top_mp = eina_mempool_add("chained_mempool", "top",
478 NULL, sizeof (Eina_Class_Top), 16);
481 ERR("ERROR: Mempool for Eina_Class_Top cannot be allocated in object init.");
485 _eina_range_mp = eina_mempool_add("chained_mempool", "range",
486 NULL, sizeof (Eina_Range), 16);
489 ERR("ERROR: Mempool for Eina_Class_Top cannot be allocated in object init.");
493 #define EMS(n) eina_magic_string_static_set(n, n ## _STR)
494 EMS(EINA_MAGIC_CLASS);
497 _eina_object_item_size = eina_mempool_alignof(sizeof (Eina_Object_Item));
502 eina_log_domain_unregister(_eina_object_log_dom);
503 _eina_object_log_dom = -1;
507 eina_mempool_del(_eina_top_mp);
513 eina_mempool_del(_eina_class_mp);
514 _eina_class_mp = NULL;
521 eina_object_shutdown(void)
523 eina_mempool_del(_eina_class_mp);
524 eina_mempool_del(_eina_top_mp);
526 eina_log_domain_unregister(_eina_object_log_dom);
527 _eina_object_log_dom = -1;
532 eina_class_new(const char *name,
533 unsigned int class_size,
534 unsigned int pool_size,
535 Eina_Class_Callback constructor,
536 Eina_Class_Callback destructor,
541 unsigned int object_size = class_size;
543 if (parent) EINA_MAGIC_CHECK_CLASS(parent, NULL);
545 c = eina_mempool_malloc(_eina_class_mp, sizeof (Eina_Class));
551 parent->childs = eina_inlist_append(parent->childs,
553 c->top = parent->top;
557 c->top = eina_mempool_malloc(_eina_top_mp, sizeof (Eina_Class_Top));
559 c->top->top_parent = c;
560 c->top->range = NULL;
561 c->top->available = NULL;
562 c->top->upper_limit = 0;
565 /* Build complete object size and find top parent */
566 if (parent) object_size += parent->object_size;
568 c->name = eina_stringshare_add(name);
569 c->class_size = class_size;
570 c->pool_size = pool_size;
571 c->object_size = object_size;
572 c->mempool = eina_mempool_add("chained_mempool", "range",
574 _eina_object_item_size + object_size, pool_size);
576 c->constructor = constructor;
577 c->destructor = destructor;
580 c->allocated_range = NULL;
583 #ifdef EINA_HAVE_DEBUG_THREADS
584 c->self = pthread_self();
586 eina_lock_new(&c->mutex);
588 EINA_MAGIC_SET(c, EINA_MAGIC_CLASS);
594 eina_class_name_get(Eina_Class *class)
596 EINA_MAGIC_CHECK_CLASS(class, NULL);
602 eina_class_size_get(Eina_Class *class)
604 EINA_MAGIC_CHECK_CLASS(class, 0);
606 return class->class_size;
610 eina_class_object_size_get(Eina_Class *class)
612 EINA_MAGIC_CHECK_CLASS(class, 0);
614 return class->object_size;
618 eina_class_del(Eina_Class *class)
620 EINA_MAGIC_CHECK_CLASS(class);
622 EINA_MAGIC_SET(class, 0);
624 while (class->allocated_range)
625 _eina_class_range_del(EINA_INLIST_CONTAINER_GET(class->allocated_range,
628 while (class->childs)
632 child = EINA_INLIST_CONTAINER_GET(class->childs, Eina_Class);
633 eina_class_del(child);
638 class->parent->childs = eina_inlist_remove(class->parent->childs,
639 EINA_INLIST_GET(class));
643 while (class->top->available)
647 range = EINA_INLIST_CONTAINER_GET(class->top->available, Eina_Range);
648 class->top->available = eina_inlist_remove(class->top->available,
649 class->top->available);
650 eina_mempool_free(_eina_range_mp, range);
654 #ifdef EINA_HAVE_DEBUG_THREADS
655 assert(pthread_equal(class->self, pthread_self()));
657 eina_lock_free(&class->mutex);
659 eina_mempool_del(class->mempool);
660 eina_mempool_free(_eina_class_mp, class);
664 eina_class_repack(Eina_Class *class)
668 EINA_MAGIC_CHECK_CLASS(class);
670 if (!eina_lock_take(&class->mutex))
672 #ifdef EINA_HAVE_DEBUG_THREADS
673 assert(pthread_equal(class->self, pthread_self()));
677 eina_mempool_repack(class->mempool, _eina_class_range_repack, class);
679 eina_lock_release(&class->mutex);
681 EINA_INLIST_FOREACH(class->childs, child)
682 eina_class_repack(child);
686 eina_object_add(Eina_Class *class)
688 Eina_Class_Range *range;
689 Eina_Object_Item **object;
692 EINA_MAGIC_CHECK_CLASS(class, NULL);
694 /* No need to lock the class as we don't access/modify the pointer inside
697 range = _eina_class_empty_range_get(class->allocated_range);
698 if (!range) range = _eina_class_range_add(class);
700 if (range->empty_count == 0)
702 ERR("The impossible happen, range is empty when it should not !");
706 range->empty_count--;
708 object = eina_trash_pop(&range->empty);
709 if (!object) object = range->pointer_array + range->current++;
711 localid = object - range->pointer_array;
713 *object = eina_mempool_malloc(class->mempool,
714 class->object_size + _eina_object_item_size);
716 (*object)->index = localid;
717 (*object)->link = NULL;
718 (*object)->range = range;
720 _eina_object_constructor_call(class,
721 ((unsigned char*)(*object)) + _eina_object_item_size);
723 if (!(++range->generation_array[localid]))
724 ++range->generation_array[localid];
726 return _eina_object_get(*object);
730 eina_object_pointer_get(Eina_Class *class,
733 Eina_Object_Item *item;
734 unsigned char *mem = NULL;
736 if (!object) return NULL;
737 EINA_MAGIC_CHECK_CLASS(class, NULL);
739 if (!eina_lock_take(&class->mutex))
741 #ifdef EINA_HAVE_DEBUG_THREADS
742 assert(pthread_equal(class->self, pthread_self()));
746 item = _eina_object_find_item(class, object);
747 if (!item) goto on_error;
749 mem = (unsigned char*) item + _eina_object_item_size;
752 eina_lock_release(&class->mutex);
758 eina_object_del(Eina_Class *class,
761 Eina_Object_Item *item;
763 if (!object) return ;
764 EINA_MAGIC_CHECK_CLASS(class);
766 if (!eina_lock_take(&class->mutex))
768 #ifdef EINA_HAVE_DEBUG_THREADS
769 assert(pthread_equal(class->self, pthread_self()));
773 item = _eina_object_find_item(class, object);
774 if (!item) goto on_error;
776 _eina_object_item_del(item);
779 eina_lock_release(&class->mutex);
783 eina_object_parent_set(Eina_Class *parent_class, Eina_Object *parent,
784 Eina_Class *object_class, Eina_Object *object)
786 Eina_Object_Item *parent_item;
787 Eina_Object_Item *object_item;
789 if (!parent) return EINA_FALSE;
790 if (!object) return EINA_FALSE;
791 EINA_MAGIC_CHECK_CLASS(parent_class, EINA_FALSE);
792 EINA_MAGIC_CHECK_CLASS(object_class, EINA_FALSE);
794 if (!eina_lock_take(&parent_class->mutex))
796 #ifdef EINA_HAVE_DEBUG_THREADS
797 assert(pthread_equal(parent_class->self, pthread_self()));
801 if (!eina_lock_take(&object_class->mutex))
803 #ifdef EINA_HAVE_DEBUG_THREADS
804 assert(pthread_equal(object_class->self, pthread_self()));
808 parent_item = _eina_object_find_item(parent_class, parent);
809 if (!parent_item) return EINA_FALSE;
811 object_item = _eina_object_find_item(object_class, object);
812 if (!object_item) return EINA_FALSE;
814 if (object_item->parent)
815 object_item->parent->link = eina_inlist_remove(object_item->parent->link,
816 EINA_INLIST_GET(object_item));
818 object_item->parent = parent_item;
819 parent_item->link = eina_inlist_append(parent_item->link,
820 EINA_INLIST_GET(object_item));
822 eina_lock_release(&parent_class->mutex);
823 eina_lock_release(&object_class->mutex);
829 eina_object_parent_get(Eina_Class *class, Eina_Object *object)
831 Eina_Object_Item *object_item;
832 Eina_Object *or = NULL;
834 if (!object) return EINA_FALSE;
835 EINA_MAGIC_CHECK_CLASS(class, EINA_FALSE);
837 if (!eina_lock_take(&class->mutex))
839 #ifdef EINA_HAVE_DEBUG_THREADS
840 assert(pthread_equal(class->self, pthread_self()));
844 object_item = _eina_object_find_item(class, object);
846 or = _eina_object_get(object_item->parent);
848 eina_lock_release(&class->mutex);