free(p);
}
+static int
+_eina_chained_mempool_usage_cmp(const Eina_Inlist *l1, const Eina_Inlist *l2)
+{
+ const Chained_Pool *p1;
+ const Chained_Pool *p2;
+
+ p1 = EINA_INLIST_CONTAINER_GET(l1, const Chained_Pool);
+ p2 = EINA_INLIST_CONTAINER_GET(l2, const Chained_Pool);
+
+ return p2->usage - p1->usage;
+}
+
+static void *
+_eina_chained_mempool_alloc_in(Chained_Mempool *pool, Chained_Pool *p)
+{
+ void *mem;
+
+ if (p->last)
+ {
+ mem = p->last;
+ p->last += pool->item_alloc;
+ if (p->last >= p->limit)
+ p->last = NULL;
+ }
+ else
+ {
+#ifndef NVALGRIND
+ VALGRIND_MAKE_MEM_DEFINED(p->base, pool->item_alloc);
+#endif
+ // Request a free pointer
+ mem = eina_trash_pop(&p->base);
+ }
+
+ // move to end - it just filled up
+ if (!p->base && !p->last)
+ pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p));
+
+ p->usage++;
+ pool->usage++;
+
+#ifndef NVALGRIND
+ VALGRIND_MEMPOOL_ALLOC(pool, mem, pool->item_alloc);
+#endif
+
+ return mem;
+}
+
+static Eina_Bool
+_eina_chained_mempool_free_in(Chained_Mempool *pool, Chained_Pool *p, void *ptr)
+{
+ void *pmem;
+
+ // pool mem base
+ pmem = (void *)(((unsigned char *)p) + sizeof(Chained_Pool));
+
+ // is it in pool mem?
+ if (ptr < pmem)
+ {
+#ifdef DEBUG
+ INF("%p is inside the private part of %p pool from %p Chained_Mempool (could be the sign of a buffer underrun).", ptr, p, pool);
+#endif
+ return EINA_FALSE;
+ }
+
+ // freed node points to prev free node
+ eina_trash_push(&p->base, ptr);
+ // next free node is now the one we freed
+ p->usage--;
+ pool->usage--;
+ if (p->usage == 0)
+ {
+ // free bucket
+ pool->first = eina_inlist_remove(pool->first, EINA_INLIST_GET(p));
+ pool->root = eina_rbtree_inline_remove(pool->root, EINA_RBTREE_GET(p),
+ _eina_chained_mp_pool_cmp, NULL);
+ _eina_chained_mp_pool_free(p);
+
+ return EINA_TRUE;
+ }
+ else
+ {
+ // move to front
+ pool->first = eina_inlist_promote(pool->first, EINA_INLIST_GET(p));
+ }
+
+ return EINA_FALSE;
+}
+
static void *
eina_chained_mempool_malloc(void *data, __UNUSED__ unsigned int size)
{
_eina_chained_mp_pool_cmp, NULL);
}
- if (p->last)
- {
- mem = p->last;
- p->last += pool->item_alloc;
- if (p->last >= p->limit)
- p->last = NULL;
- }
- else
- {
-#ifndef NVALGRIND
- VALGRIND_MAKE_MEM_DEFINED(p->base, pool->item_alloc);
-#endif
- // Request a free pointer
- mem = eina_trash_pop(&p->base);
- }
-
- // move to end - it just filled up
- if (!p->base && !p->last)
- pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p));
-
- p->usage++;
- pool->usage++;
+ mem = _eina_chained_mempool_alloc_in(pool, p);
#ifdef EFL_HAVE_THREADS
if (_threads_activated)
}
#endif
-#ifndef NVALGRIND
- VALGRIND_MEMPOOL_ALLOC(pool, mem, pool->item_alloc);
-#endif
-
return mem;
}
Chained_Mempool *pool = data;
Eina_Rbtree *r;
Chained_Pool *p;
- void *pmem;
// look 4 pool
p = EINA_RBTREE_CONTAINER_GET(r, Chained_Pool);
- // pool mem base
- pmem = (void *)(((unsigned char *)p) + sizeof(Chained_Pool));
+ _eina_chained_mempool_free_in(pool, p, ptr);
- // is it in pool mem?
- if (ptr < pmem)
+ on_error:
+#ifndef NVALGRIND
+ if (ptr)
{
-#ifdef DEBUG
- INF("%p is inside the private part of %p pool from %p Chained_Mempool (could be the sign of a buffer underrun).", ptr, p, pool);
-#endif
- goto on_error;
+ VALGRIND_MEMPOOL_FREE(pool, ptr);
}
+#endif
- // freed node points to prev free node
- eina_trash_push(&p->base, ptr);
- // next free node is now the one we freed
- p->usage--;
- pool->usage--;
- if (p->usage == 0)
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
{
- // free bucket
- pool->first = eina_inlist_remove(pool->first, EINA_INLIST_GET(p));
- pool->root = eina_rbtree_inline_remove(pool->root, EINA_RBTREE_GET(p),
- _eina_chained_mp_pool_cmp, NULL);
- _eina_chained_mp_pool_free(p);
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_unlock(&pool->mutex);
+# else
+ ReleaseMutex(pool->mutex);
+# endif
}
- else
+#endif
+ return;
+}
+
+static void
+eina_chained_mempool_repack(void *data,
+ Eina_Mempool_Repack_Cb cb,
+ void *cb_data)
+{
+ Chained_Mempool *pool = data;
+ Chained_Pool *start;
+ Chained_Pool *tail;
+
+ /* FIXME: Improvement - per Chained_Pool lock */
+
+#ifdef EFL_HAVE_THREADS
+ if (_threads_activated)
{
- // move to front
- pool->first = eina_inlist_promote(pool->first, EINA_INLIST_GET(p));
+# ifdef EFL_HAVE_POSIX_THREADS
+ pthread_mutex_lock(&pool->mutex);
+# else
+ WaitForSingleObject(pool->mutex, INFINITE);
+# endif
}
+#ifdef EFL_DEBUG_THREADS
+ else
+ assert(pthread_equal(pool->self, pthread_self()));
+#endif
+#endif
- on_error:
-#ifndef NVALGRIND
- if (ptr)
+ pool->first = eina_inlist_sort(pool->first,
+ (Eina_Compare_Cb) _eina_chained_mempool_usage_cmp);
+
+ /*
+ idea : remove the almost empty pool at the beginning of the list by
+ moving data in the last pool with empty slot
+ */
+ tail = EINA_INLIST_CONTAINER_GET(pool->first->last, Chained_Pool);
+ while (tail && tail->usage == pool->pool_size)
+ tail = EINA_INLIST_CONTAINER_GET((EINA_INLIST_GET(tail)->prev), Chained_Pool);
+
+ while (tail)
{
- VALGRIND_MEMPOOL_FREE(pool, ptr);
+ unsigned char *src;
+ unsigned char *dst;
+
+ start = EINA_INLIST_CONTAINER_GET(pool->first, Chained_Pool);
+
+ if (start == tail || start->usage == pool->pool_size)
+ break;
+
+ for (src = start->limit - pool->group_size;
+ src != start->limit;
+ src += pool->item_alloc)
+ {
+ Eina_Bool is_free = EINA_FALSE;
+ Eina_Bool is_dead;
+
+ /* Do we have something inside that piece of memory */
+ if (start->last != NULL && src >= start->last)
+ {
+ is_free = EINA_TRUE;
+ }
+ else
+ {
+ Eina_Trash *over = start->base;
+
+ while (over != NULL && (unsigned char*) over != src)
+ over = over->next;
+
+ if (over == NULL)
+ is_free = EINA_TRUE;
+ }
+
+ if (is_free) continue ;
+
+ /* get a new memory pointer from the latest most occuped pool */
+ dst = _eina_chained_mempool_alloc_in(pool, tail);
+ /* move data from one to another */
+ memcpy(dst, src, pool->item_alloc);
+ /* notify caller */
+ cb(dst, src, cb_data);
+ /* destroy old pointer */
+ is_dead = _eina_chained_mempool_free_in(pool, start, src);
+
+ /* search last tail with empty slot */
+ while (tail && tail->usage == pool->pool_size)
+ tail = EINA_INLIST_CONTAINER_GET((EINA_INLIST_GET(tail)->prev),
+ Chained_Pool);
+ /* no more free space */
+ if (!tail || tail == start) break;
+ if (is_dead) break;
+ }
}
-#endif
+
+ /* FIXME: improvement - reorder pool so that the most used one get in front */
#ifdef EFL_HAVE_THREADS
if (_threads_activated)
# endif
}
#endif
- return;
}
static void *
&eina_chained_mempool_realloc,
NULL,
NULL,
- &eina_chained_mempool_shutdown
+ &eina_chained_mempool_shutdown,
+ &eina_chained_mempool_repack
};
Eina_Bool chained_init(void)