--- /dev/null
+/* EINA - EFL data type library
+ * Copyright (C) 2011 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#ifdef EFL_HAVE_POSIX_THREADS
+#include <pthread.h>
+
+# ifdef EFL_DEBUG_THREADS
+# include <assert.h>
+# endif
+#endif
+
+#ifdef EFL_HAVE_WIN32_THREADS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+#include "eina_object.h"
+
+#include "eina_private.h"
+#include "eina_inlist.h"
+#include "eina_rbtree.h"
+#include "eina_mempool.h"
+#include "eina_trash.h"
+#include "eina_log.h"
+#include "eina_stringshare.h"
+
+/*============================================================================*
+ * Local *
+ *============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+/* If we are on a 64bits computer user bigger generation and ID */
+/* FIXME: make it GCC independant */
+/* FIXME: maybe having 2^32 objects doesn't make sense and 2^24 are enough
+ so instead of increasing the object count, we could just add a magic
+ to first check if the pointer is valid at all (and maybe use a pointer
+ that will always trigger a segv if we try to use it directly).
+*/
+#ifdef _LP64
+typedef unsigned long Eina_Object_ID;
+typedef unsigned short Eina_Object_Generation;
+#define EINA_GEN_OFFSET 48
+#else
+typedef unsigned int Eina_Object_ID;
+typedef unsigned char Eina_Object_Generation;
+#define EINA_GEN_OFFSET 24
+#endif
+
+typedef struct _Eina_Class_Range Eina_Class_Range;
+typedef struct _Eina_Object_Item Eina_Object_Item;
+typedef struct _Eina_Range Eina_Range;
+typedef struct _Eina_Class_Top Eina_Class_Top;
+
+struct _Eina_Range
+{
+ EINA_INLIST;
+
+ unsigned int start;
+ unsigned int end;
+};
+
+struct _Eina_Object_Item
+{
+ EINA_INLIST;
+ Eina_Class_Range *range;
+
+ Eina_Object_Item *parent;
+ Eina_Inlist *link;
+
+ unsigned int index;
+};
+
+struct _Eina_Class_Range
+{
+ EINA_INLIST;
+ EINA_RBTREE;
+
+ unsigned int start;
+ unsigned int end;
+ unsigned int current;
+ unsigned int empty_count;
+
+ Eina_Class *type;
+ Eina_Trash *empty;
+
+ Eina_Object_Item **pointer_array;
+ Eina_Object_Generation generation_array[1];
+};
+
+struct _Eina_Class_Top
+{
+ Eina_Class *top_parent;
+ Eina_Rbtree *range;
+ Eina_Inlist *available;
+
+ unsigned int upper_limit;
+};
+
+struct _Eina_Class
+{
+ EINA_INLIST;
+
+ const char *name;
+
+ Eina_Class_Top *top;
+
+ Eina_Class *parent;
+ Eina_Inlist *childs;
+
+ Eina_Class_Callback constructor;
+ Eina_Class_Callback destructor;
+ void *data;
+
+ Eina_Inlist *allocated_range;
+
+ Eina_Mempool *mempool;
+ unsigned int class_size;
+ unsigned int object_size;
+ unsigned int pool_size;
+
+ Eina_Bool repack_needed : 1;
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+# ifdef EFL_DEBUG_THREADS
+ pthread_t self;
+# endif
+ pthread_mutex_t mutex;
+# else
+ HANDLE mutex;
+# endif
+#endif
+
+ EINA_MAGIC;
+};
+
+static const char EINA_MAGIC_CLASS_STR[] = "Eina Class";
+
+static Eina_Mempool *_eina_class_mp = NULL;
+static Eina_Mempool *_eina_top_mp = NULL;
+static Eina_Mempool *_eina_range_mp = NULL;
+static int _eina_object_log_dom = -1;
+static unsigned int _eina_object_item_size = 0;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_object_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_object_log_dom, __VA_ARGS__)
+
+#define EINA_MAGIC_CHECK_CLASS(d, ...) \
+ do { \
+ if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_CLASS)) \
+ { \
+ EINA_MAGIC_FAIL(d, EINA_MAGIC_CLASS); \
+ return __VA_ARGS__; \
+ } \
+ } while(0)
+
+static int
+_eina_rbtree_cmp_range(const Eina_Rbtree *node, const void *key,
+ __UNUSED__ int length, __UNUSED__ void *data)
+{
+ Eina_Class_Range *range;
+ Eina_Object_ID id;
+
+ range = EINA_RBTREE_CONTAINER_GET(node, Eina_Class_Range);
+ id = (Eina_Object_ID) key;
+
+ if (id < range->start) return -1;
+ else if (id >= range->end) return 1;
+ return 0;
+}
+
+static Eina_Rbtree_Direction
+_eina_class_direction_range(const Eina_Rbtree *left,
+ const Eina_Rbtree *right,
+ __UNUSED__ void *data)
+{
+ Eina_Class_Range *rl;
+ Eina_Class_Range *rr;
+
+ rl = EINA_RBTREE_CONTAINER_GET(left, Eina_Class_Range);
+ rr = EINA_RBTREE_CONTAINER_GET(right, Eina_Class_Range);
+
+ if (rl->start < rr->start) return EINA_RBTREE_LEFT;
+ return EINA_RBTREE_RIGHT;
+}
+
+/* really destroying a range and handling that case is a complex
+ problem to solve. Not handling it right now, exposing myself to
+ DoS. */
+static void
+_eina_range_cleanup(Eina_Class_Range *range)
+{
+ range->current = 0;
+ range->empty = NULL;
+}
+
+static void
+_eina_object_constructor_call(Eina_Class *class, void *object)
+{
+ if (class->parent) _eina_object_constructor_call(class->parent, object);
+
+ if (class->constructor) class->constructor(class, object, class->data);
+}
+
+static void
+_eina_object_destructor_call(Eina_Class *class, void *object)
+{
+ if (class->destructor) class->destructor(class, object, class->data);
+
+ if (class->parent) _eina_object_destructor_call(class->parent, object);
+}
+
+static Eina_Object*
+_eina_object_get(Eina_Object_Item *item)
+{
+ Eina_Object_Generation gen;
+ Eina_Object_ID id;
+
+ if (!item) return NULL;
+
+ gen = item->range->generation_array[item->index];
+
+ id = (gen << EINA_GEN_OFFSET) + item->range->start + item->index;
+
+ return (Eina_Object *) id;
+}
+
+static Eina_Object_Item *
+_eina_object_find_item(Eina_Class *class, Eina_Object *object)
+{
+ Eina_Class_Range *matched;
+ Eina_Object_Item *item;
+ Eina_Class *search;
+ Eina_Rbtree *match;
+ Eina_Object_Generation generation;
+ Eina_Object_ID id;
+ int idx;
+
+ id = (Eina_Object_ID) object;
+ idx = id & ((1 << EINA_GEN_OFFSET) - 1);
+ generation = id & !((1 << EINA_GEN_OFFSET) - 1);
+
+ /* Try to find the ID */
+ match = eina_rbtree_inline_lookup(class->top->range,
+ (void*) idx, sizeof (Eina_Object_ID),
+ _eina_rbtree_cmp_range, NULL);
+ /* ID not found, invalid pointer ! */
+ if (!match)
+ {
+ ERR("%p: ID [%i] not found in class hiearchy of [%s].",
+ object, idx, class->name);
+ return NULL;
+ }
+ matched = EINA_RBTREE_CONTAINER_GET(match, Eina_Class_Range);
+
+ /* generation mismatch, invalid pointer ! */
+ if (generation != matched->generation_array[idx - matched->start])
+ {
+ ERR("%p: generation mismatch [%i] vs [%i].",
+ object, generation, matched->generation_array[idx - matched->start]);
+ return NULL;
+ }
+
+ /* does it belong to the right class ? */
+ for (search = matched->type; search && search != class; search = search->parent)
+ ;
+
+ /* class match request or invalid pointer ! */
+ if (search != class)
+ {
+ ERR("%p: from class [%s] is not in the hierarchie of [%s].",
+ object, matched->type->name, class->name);
+ return NULL;
+ }
+
+ /* retrieve pointer */
+ item = matched->pointer_array[id - matched->start];
+ /* allocated or invalid pointer ? */
+ if (!item)
+ {
+ ERR("%p: ID is not allocated.", object);
+ return NULL;
+ }
+
+ /* be aware, that because we use eina_trash to store empty pointer after first use,
+ pointer could be != NULL and still be empty. Need another pool of data somewhere
+ to store the state of the allocation... Is it needed ?
+ */
+
+ return item;
+}
+
+static void
+_eina_object_item_del(Eina_Object_Item *item)
+{
+ Eina_Class *class;
+
+ class = item->range->type;
+ _eina_object_destructor_call(class,
+ (unsigned char*) item + _eina_object_item_size);
+
+ eina_trash_push(&item->range->empty, item->range->pointer_array + item->index);
+ item->range->generation_array[item->index]++;
+ item->range->empty_count++;
+
+ if (item->range->empty_count == item->range->end - item->range->start)
+ _eina_range_cleanup(item->range);
+ item->range = NULL;
+
+ if (item->parent)
+ item->parent->link = eina_inlist_remove(item->parent->link, EINA_INLIST_GET(item));
+ item->parent = NULL;
+
+ while (item->link)
+ {
+ Eina_Object_Item *child;
+
+ child = EINA_INLIST_CONTAINER_GET(item->link, Eina_Object_Item);
+ _eina_object_item_del(child);
+ }
+
+ eina_mempool_free(class->mempool, item);
+}
+
+static Eina_Class_Range *
+_eina_class_empty_range_get(Eina_Inlist *list)
+{
+ while (list)
+ {
+ Eina_Class_Range *range;
+
+ range = EINA_INLIST_CONTAINER_GET(list, Eina_Class_Range);
+ if (range->empty_count > 0)
+ return range;
+
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+static Eina_Class_Range *
+_eina_class_range_add(Eina_Class *class)
+{
+ Eina_Class_Range *range;
+ unsigned char *tmp;
+ Eina_Range *av = NULL;
+
+ range = malloc(sizeof (Eina_Class_Range)
+ + sizeof (Eina_Object_Item*) * class->pool_size
+ + sizeof (Eina_Object_Generation) * (class->pool_size - 1));
+ if (!range) return NULL;
+
+ tmp = (void*) (range + 1);
+ tmp += sizeof (Eina_Object_Generation) * (class->pool_size - 1);
+
+ range->pointer_array = (Eina_Object_Item**) tmp;
+
+ /* no need to fix generation to a specific value as random value should be just fine */
+ memset(range->pointer_array, 0, sizeof (Eina_Object_Item*) * class->pool_size);
+
+ range->current = 0;
+ range->type = class;
+ range->empty = NULL;
+
+ /* and now find an empty block */
+ EINA_INLIST_FOREACH(class->top->available, av)
+ if (av->end - av->start == class->pool_size)
+ break;
+
+ if (av)
+ {
+ range->start = av->start;
+ range->end = av->end;
+
+ class->top->available = eina_inlist_remove(class->top->available,
+ EINA_INLIST_GET(av));
+ eina_mempool_free(_eina_range_mp, av);
+ }
+ else
+ {
+ range->start = class->top->upper_limit;
+ range->end = range->start + class->pool_size;
+ class->top->upper_limit = range->end;
+ }
+
+ return range;
+}
+
+static void
+_eina_class_range_del(Eina_Class_Range *range)
+{
+ Eina_Class_Top *top;
+ Eina_Range *keep;
+
+ keep = eina_mempool_malloc(_eina_range_mp, sizeof (Eina_Range));
+ if (!keep)
+ {
+ ERR("Not enougth memory to keep track of allocated range.");
+ goto delete_class_range;
+ }
+
+ keep->start = range->start;
+ keep->end = range->end;
+
+ top = range->type->top;
+
+ top->available = eina_inlist_prepend(top->available,
+ EINA_INLIST_GET(keep));
+
+ delete_class_range:
+ top->range = eina_rbtree_inline_remove(top->range, EINA_RBTREE_GET(range),
+ _eina_class_direction_range, NULL);
+ range->type->allocated_range = eina_inlist_remove(range->type->allocated_range,
+ EINA_INLIST_GET(range));
+ free(range);
+}
+
+static void
+_eina_class_range_repack(void *dst, void *src, void *data)
+{
+ Eina_Object_Item *di = dst;
+ Eina_Object_Item *si = src;
+ Eina_Class *class = data;
+
+ (void) class;
+
+ si->range->pointer_array[si->index] = di;
+
+ /* FIXME: We could just lock the right Eina_Class_Range
+ here instead of locking all the Class */
+}
+
+/**
+ * @}
+ */
+
+Eina_Bool
+eina_object_init(void)
+{
+ _eina_object_log_dom = eina_log_domain_register("eina_object",
+ EINA_LOG_COLOR_DEFAULT);
+ if (_eina_object_log_dom < 0)
+ {
+ EINA_LOG_ERR("Could not register log domain: eina_list");
+ return EINA_FALSE;
+ }
+
+ _eina_class_mp = eina_mempool_add("chained_mempool", "class",
+ NULL, sizeof (Eina_Class), 64);
+ if (!_eina_class_mp)
+ {
+ ERR("ERROR: Mempool for Eina_Class cannot be allocated in object init.");
+ goto on_init_fail;
+ }
+
+ _eina_top_mp = eina_mempool_add("chained_mempool", "top",
+ NULL, sizeof (Eina_Class_Top), 64);
+ if (!_eina_top_mp)
+ {
+ ERR("ERROR: Mempool for Eina_Class_Top cannot be allocated in object init.");
+ goto on_init_fail;
+ }
+
+ _eina_range_mp = eina_mempool_add("chained_mempool", "range",
+ NULL, sizeof (Eina_Range), 64);
+ if (!_eina_range_mp)
+ {
+ ERR("ERROR: Mempool for Eina_Class_Top cannot be allocated in object init.");
+ goto on_init_fail;
+ }
+
+#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
+ EMS(EINA_MAGIC_CLASS);
+#undef EMS
+
+ _eina_object_item_size = eina_mempool_alignof(sizeof (Eina_Object_Item));
+
+ return EINA_TRUE;
+
+ on_init_fail:
+ eina_log_domain_unregister(_eina_object_log_dom);
+ _eina_object_log_dom = -1;
+
+ if (_eina_top_mp)
+ {
+ eina_mempool_del(_eina_top_mp);
+ _eina_top_mp = NULL;
+ }
+
+ if (_eina_class_mp)
+ {
+ eina_mempool_del(_eina_class_mp);
+ _eina_class_mp = NULL;
+ }
+
+ return EINA_FALSE;
+}
+
+Eina_Bool
+eina_object_shutdown(void)
+{
+ eina_mempool_del(_eina_class_mp);
+ eina_mempool_del(_eina_top_mp);
+
+ eina_log_domain_unregister(_eina_object_log_dom);
+ _eina_object_log_dom = -1;
+ return EINA_TRUE;
+}
+
+Eina_Class *
+eina_class_new(const char *name,
+ unsigned int class_size,
+ unsigned int pool_size,
+ Eina_Class_Callback constructor,
+ Eina_Class_Callback destructor,
+ Eina_Class *parent,
+ void *data)
+{
+ Eina_Class *c;
+ unsigned int object_size = class_size;
+
+ if (parent) EINA_MAGIC_CHECK_CLASS(parent, NULL);
+
+ c = eina_mempool_malloc(_eina_class_mp, sizeof (Eina_Class));
+ if (!c) return NULL;
+
+ c->parent = parent;
+ if (parent)
+ {
+ parent->childs = eina_inlist_append(parent->childs,
+ EINA_INLIST_GET(c));
+ c->top = parent->top;
+ }
+ else
+ {
+ c->top = eina_mempool_malloc(_eina_top_mp, sizeof (Eina_Class_Top));
+
+ c->top->top_parent = c;
+ c->top->range = NULL;
+ c->top->available = NULL;
+ c->top->upper_limit = 0;
+ }
+
+ /* Build complete object size and find top parent */
+ if (parent) object_size += parent->object_size;
+
+ c->name = eina_stringshare_add(name);
+ c->class_size = class_size;
+ c->pool_size = pool_size;
+ c->object_size = object_size;
+ c->mempool = eina_mempool_add("chained_mempool", "range",
+ NULL,
+ _eina_object_item_size + object_size, pool_size);
+
+ c->constructor = constructor;
+ c->destructor = destructor;
+ c->data = data;
+
+ c->allocated_range = NULL;
+ c->childs = NULL;
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+# ifdef EFL_DEBUG_THREADS
+ c->self = pthread_self();
+# endif
+ pthread_mutex_init(&c->mutex, NULL);
+# else
+ c->mutex = CreateMutex(NULL, FALSE, NULL);
+# endif
+#endif
+
+ EINA_MAGIC_SET(c, EINA_MAGIC_CLASS);
+
+ return c;
+}
+
+const char *
+eina_class_name_get(Eina_Class *class)
+{
+ EINA_MAGIC_CHECK_CLASS(class, NULL);
+
+ return class->name;
+}
+
+unsigned int
+eina_class_size_get(Eina_Class *class)
+{
+ EINA_MAGIC_CHECK_CLASS(class, 0);
+
+ return class->class_size;
+}
+
+unsigned int
+eina_class_object_size_get(Eina_Class *class)
+{
+ EINA_MAGIC_CHECK_CLASS(class, 0);
+
+ return class->object_size;
+}
+
+void
+eina_class_del(Eina_Class *class)
+{
+ EINA_MAGIC_CHECK_CLASS(class);
+
+ EINA_MAGIC_SET(class, 0);
+
+ while (class->allocated_range)
+ _eina_class_range_del(EINA_INLIST_CONTAINER_GET(class->allocated_range,
+ Eina_Class_Range));
+
+ while (class->childs)
+ {
+ Eina_Class *child;
+
+ child = EINA_INLIST_CONTAINER_GET(class->childs, Eina_Class);
+ eina_class_del(child);
+ }
+
+ if (class->parent)
+ {
+ class->parent->childs = eina_inlist_remove(class->parent->childs,
+ EINA_INLIST_GET(class));
+ }
+ else
+ {
+ while (class->top->available)
+ {
+ Eina_Range *range;
+
+ range = EINA_INLIST_CONTAINER_GET(class->top->available, Eina_Range);
+ class->top->available = eina_inlist_remove(class->top->available,
+ class->top->available);
+ eina_mempool_free(_eina_range_mp, range);
+ }
+ }
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+# ifdef EFL_DEBUG_THREADS
+ assert(pthread_equal(class->self, pthread_self()));
+# endif
+ pthread_mutex_destroy(&class->mutex);
+# else
+ CloseHandle(class->mutex);
+# endif
+#endif
+
+ eina_mempool_del(class->mempool);
+ eina_mempool_free(_eina_class_mp, class);
+}
+
+void
+eina_class_repack(Eina_Class *class)
+{
+ Eina_Class *child;
+
+ EINA_MAGIC_CHECK_CLASS(class);
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_lock(&class->mutex);
+# else
+ WaitForSingleObject(class->mutex, INFINITE);
+# endif
+ }
+#ifdef EFL_DEBUG_THREADS
+ else
+ assert(pthread_equal(class->self, pthread_self()));
+#endif
+#endif
+
+ eina_mempool_repack(class->mempool, _eina_class_range_repack, class);
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_unlock(&class->mutex);
+# else
+ ReleaseMutex(class->mutex);
+# endif
+ }
+#endif
+
+ EINA_INLIST_FOREACH(class->childs, child)
+ eina_class_repack(child);
+}
+
+Eina_Object *
+eina_object_add(Eina_Class *class)
+{
+ Eina_Class_Range *range;
+ Eina_Object_Item **object;
+ int localid;
+
+ EINA_MAGIC_CHECK_CLASS(class, NULL);
+
+ /* No need to lock the class as we don't access/modify the pointer inside
+ * this function. */
+
+ range = _eina_class_empty_range_get(class->allocated_range);
+ if (!range) range = _eina_class_range_add(class);
+
+ if (range->empty_count == 0)
+ {
+ ERR("The impossible happen, range is empty when it should not !");
+ return NULL;
+ }
+
+ range->empty_count--;
+
+ object = eina_trash_pop(&range->empty);
+ if (!object) object = range->pointer_array + range->current++;
+
+ localid = object - range->pointer_array;
+
+ *object = eina_mempool_malloc(class->mempool,
+ class->object_size + _eina_object_item_size);
+
+ (*object)->index = localid;
+ (*object)->link = NULL;
+ (*object)->range = range;
+
+ _eina_object_constructor_call(class,
+ ((unsigned char*)(*object)) + _eina_object_item_size);
+
+ if (!(++range->generation_array[localid]))
+ ++range->generation_array[localid];
+
+ return _eina_object_get(*object);
+}
+
+void *
+eina_object_pointer_get(Eina_Class *class,
+ Eina_Object *object)
+{
+ Eina_Object_Item *item;
+ unsigned char *mem = NULL;
+
+ if (!object) return NULL;
+ EINA_MAGIC_CHECK_CLASS(class, NULL);
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_lock(&class->mutex);
+# else
+ WaitForSingleObject(class->mutex, INFINITE);
+# endif
+ }
+#ifdef EFL_DEBUG_THREADS
+ else
+ assert(pthread_equal(class->self, pthread_self()));
+#endif
+#endif
+
+ item = _eina_object_find_item(class, object);
+ if (!item) goto on_error;
+
+ mem = (unsigned char*) item + _eina_object_item_size;
+
+ on_error:
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_unlock(&class->mutex);
+# else
+ ReleaseMutex(class->mutex);
+# endif
+ }
+#endif
+
+ return mem;
+}
+
+void
+eina_object_del(Eina_Class *class,
+ Eina_Object *object)
+{
+ Eina_Object_Item *item;
+
+ if (!object) return ;
+ EINA_MAGIC_CHECK_CLASS(class);
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_lock(&class->mutex);
+# else
+ WaitForSingleObject(class->mutex, INFINITE);
+# endif
+ }
+#ifdef EFL_DEBUG_THREADS
+ else
+ assert(pthread_equal(class->self, pthread_self()));
+#endif
+#endif
+
+ item = _eina_object_find_item(class, object);
+ if (!item) goto on_error;
+
+ _eina_object_item_del(item);
+
+ on_error:
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_unlock(&class->mutex);
+# else
+ ReleaseMutex(class->mutex);
+# endif
+ }
+#endif
+}
+
+Eina_Bool
+eina_object_parent_set(Eina_Class *parent_class, Eina_Object *parent,
+ Eina_Class *object_class, Eina_Object *object)
+{
+ Eina_Object_Item *parent_item;
+ Eina_Object_Item *object_item;
+
+ if (!parent) return EINA_FALSE;
+ if (!object) return EINA_FALSE;
+ EINA_MAGIC_CHECK_CLASS(parent_class, EINA_FALSE);
+ EINA_MAGIC_CHECK_CLASS(object_class, EINA_FALSE);
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_lock(&parent_class->mutex);
+ pthread_mutex_lock(&object_class->mutex);
+# else
+ WaitForSingleObject(parent_class->mutex, INFINITE);
+ WaitForSingleObject(object_class->mutex, INFINITE);
+# endif
+ }
+#ifdef EFL_DEBUG_THREADS
+ else
+ {
+ assert(pthread_equal(object_class->self, pthread_self()));
+ assert(pthread_equal(parent_class->self, pthread_self()));
+ }
+#endif
+#endif
+
+ parent_item = _eina_object_find_item(parent_class, parent);
+ if (!parent_item) return EINA_FALSE;
+
+ object_item = _eina_object_find_item(object_class, object);
+ if (!object_item) return EINA_FALSE;
+
+ if (object_item->parent)
+ object_item->parent->link = eina_inlist_remove(object_item->parent->link,
+ EINA_INLIST_GET(object_item));
+
+ object_item->parent = parent_item;
+ parent_item->link = eina_inlist_append(parent_item->link,
+ EINA_INLIST_GET(object_item));
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_unlock(&parent_class->mutex);
+ pthread_mutex_unlock(&object_class->mutex);
+# else
+ ReleaseMutex(parent_class->mutex);
+ ReleaseMutex(object_class->mutex);
+# endif
+ }
+#endif
+
+ return EINA_TRUE;
+}
+
+Eina_Object *
+eina_object_parent_get(Eina_Class *class, Eina_Object *object)
+{
+ Eina_Object_Item *object_item;
+
+ if (!object) return EINA_FALSE;
+ EINA_MAGIC_CHECK_CLASS(class, EINA_FALSE);
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
+ {
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_lock(&class->mutex);
+# else
+ WaitForSingleObject(class->mutex, INFINITE);
+# endif
+ }
+#ifdef EFL_DEBUG_THREADS
+ else
+ assert(pthread_equal(class->self, pthread_self()));
+#endif
+#endif
+
+ object_item = _eina_object_find_item(class, object);
+ if (!object_item) return EINA_FALSE;
+
+ return _eina_object_get(object_item->parent);
+}
+