summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>2016-11-04 19:06:15 +0900
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2016-11-06 13:13:10 +0900
commit0ee33e7b4b3932756b017739b3a578c5198aa041 (patch)
tree9292cbde8aa14be4a90c86e23c070acf0044fbbb
parent8b523c21e2e54bfbf072f39f8234d3a289e3ce26 (diff)
downloadefl-0ee33e7b4b3932756b017739b3a578c5198aa041.tar.gz
eina - add a free queue (eina_freeq) for deferring frees of data
this adds eina_freeq api's for c land for deferring freeing of pointers and can be used a s a simple copy & paste drop-in for free() just to "do this later". the pointer will eveentually be freed as eina_shutdown will free the main free queue and this will in turn free everything in it. as long as the main lo0op keeps pumping things will og on the queue and then be freed from it. free queues have limits so if they get full they will clear out old pointers and free them so it won't grow without bound. the default max is 1mb of data or 16384 items whichever limit is hit first and at that point the oldest item will be freed to make room for the newest. the mainloop whenever it finishes idle enterers will add an idler to spin and free while idle. the sizes can be tuned and aruged about as to what defaults should be. this also allows for better memory debugging too by being able to fill freed memory with patterns if its small enough etc. etc. @feature
-rw-r--r--src/Makefile_Eina.am11
-rw-r--r--src/lib/eina/Eina.h1
-rw-r--r--src/lib/eina/eina_freeq.c311
-rw-r--r--src/lib/eina/eina_freeq.h276
-rw-r--r--src/lib/eina/eina_main.c3
-rw-r--r--src/tests/eina/eina_suite.c1
-rw-r--r--src/tests/eina/eina_suite.h1
-rw-r--r--src/tests/eina/eina_test_freeq.c122
8 files changed, 723 insertions, 3 deletions
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 22ade8feba..bb1037e020 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -102,7 +102,9 @@ lib/eina/eina_safepointer.h \
lib/eina/eina_inline_safepointer.x \
lib/eina/eina_slice.h \
lib/eina/eina_inline_slice.x \
-lib/eina/eina_inline_modinfo.x
+lib/eina/eina_inline_modinfo.x \
+lib/eina/eina_freeq.h
+
lib_eina_libeina_la_SOURCES = \
lib/eina/eina_abi.c \
@@ -175,7 +177,9 @@ lib/eina/eina_strbuf_common.h \
lib/eina/eina_quaternion.c \
lib/eina/eina_promise.c \
lib/eina/eina_bezier.c \
-lib/eina/eina_safepointer.c
+lib/eina/eina_safepointer.c \
+lib/eina/eina_freeq.c
+
if HAVE_WIN32
lib_eina_libeina_la_SOURCES += lib/eina/eina_file_win32.c
@@ -346,7 +350,8 @@ tests/eina/eina_test_vector.c \
tests/eina/eina_test_promise.c \
tests/eina/eina_test_bezier.c \
tests/eina/eina_test_safepointer.c \
-tests/eina/eina_test_slice.c
+tests/eina/eina_test_slice.c \
+tests/eina/eina_test_freeq.c
tests_eina_eina_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
-DTESTS_WD=\"`pwd`\" \
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index a7a364fe5b..4f1db07e65 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -272,6 +272,7 @@ extern "C" {
#include <eina_bezier.h>
#include <eina_safepointer.h>
#include <eina_slice.h>
+#include <eina_freeq.h>
#undef EAPI
#define EAPI
diff --git a/src/lib/eina/eina_freeq.c b/src/lib/eina/eina_freeq.c
new file mode 100644
index 0000000000..c259bd883e
--- /dev/null
+++ b/src/lib/eina/eina_freeq.c
@@ -0,0 +1,311 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Eina.h"
+#include "eina_private.h"
+
+#ifdef HAVE_VALGRIND
+# include <valgrind.h>
+# include <memcheck.h>
+#endif
+
+// ========================================================================= //
+
+#define ITEM_FILLPAT_MAX 0
+#define ITEM_TOTAL_MAX ( 16 * 1024)
+#define ITEM_MEM_MAX (1024 * 1024)
+#define ITEM_BLOCK_COUNT 42
+
+// ========================================================================= //
+
+typedef struct _Eina_FreeQ_Item Eina_FreeQ_Item;
+typedef struct _Eina_FreeQ_Block Eina_FreeQ_Block;
+
+// ========================================================================= //
+
+struct _Eina_FreeQ_Item
+{
+ void *ptr;
+ void (*free_func) (void *ptr);
+ size_t size;
+};
+
+struct _Eina_FreeQ_Block
+{
+ int start;
+ int end;
+ Eina_FreeQ_Block *next;
+ Eina_FreeQ_Item items[ITEM_BLOCK_COUNT];
+};
+
+struct _Eina_FreeQ
+{
+ Eina_Lock lock;
+ int count; // number of item slots used
+ int count_max; // the maximum number of slots allowed to be used
+ size_t mem_max; // the maximum amount of memory allowed to be used
+ size_t mem_total; // current total memory known about in the queue
+ Eina_FreeQ_Block *blocks; // the list of blocks of free items
+ Eina_FreeQ_Block *block_last; // the last block to append items to
+};
+
+// ========================================================================= //
+
+static Eina_FreeQ *_eina_freeq_main = NULL;
+static int _eina_freeq_bypass = -1;
+static unsigned int _eina_freeq_fillpat_max = ITEM_FILLPAT_MAX;
+static unsigned char _eina_freeq_fillpat_val = 0x55;
+static int _eina_freeq_total_max = ITEM_TOTAL_MAX;
+static size_t _eina_freeq_mem_max = ITEM_MEM_MAX;
+
+// ========================================================================= //
+
+static void
+_eina_freeq_fill_do(void *ptr, size_t size)
+{
+ if (ptr) memset(ptr, _eina_freeq_fillpat_val, size);
+}
+
+static void
+_eina_freeq_fill_check(void *ptr, void (*free_func) (void *ptr), size_t size)
+{
+ unsigned char *p0 = ptr, *p = p0, *pe = p + size;
+ for (; p < pe; p++)
+ {
+ if (*p != _eina_freeq_fillpat_val) goto err;
+ }
+ return;
+err:
+ EINA_LOG_ERR("Pointer %p size %lu freed by %p has fill error %x != %x @ %lu",
+ p0, (unsigned long)size, free_func,
+ (unsigned int)*p, (unsigned int)_eina_freeq_fillpat_val,
+ (unsigned long)(p - p0));
+}
+
+static void
+_eina_freeq_free_do(void *ptr,
+ void (*free_func) (void *ptr),
+ size_t size EINA_UNUSED)
+{
+ if ((size < _eina_freeq_fillpat_max) && (size > 0))
+ _eina_freeq_fill_check(ptr, free_func, size);
+ free_func(ptr);
+ return;
+}
+
+static Eina_Bool
+_eina_freeq_block_append(Eina_FreeQ *fq)
+{
+ Eina_FreeQ_Block *fb = malloc(sizeof(Eina_FreeQ_Block));
+ if (!fb) return EINA_FALSE;
+ fb->start = 0;
+ fb->end = 0;
+ fb->next = NULL;
+ if (!fq->blocks) fq->blocks = fb;
+ else fq->block_last->next = fb;
+ fq->block_last = fb;
+ return EINA_TRUE;
+}
+
+static void
+_eina_freeq_process(Eina_FreeQ *fq)
+{
+ Eina_FreeQ_Block *fb = fq->blocks;
+ if (!fb) return;
+ _eina_freeq_free_do(fb->items[fb->start].ptr,
+ fb->items[fb->start].free_func,
+ fb->items[fb->start].size);
+ fq->mem_total -= fb->items[fb->start].size;
+ fb->start++;
+ fq->count--;
+ if (fb->start == fb->end)
+ {
+ fq->blocks = fb->next;
+ if (!fq->blocks) fq->block_last = NULL;
+ free(fb);
+ }
+}
+
+static void
+_eina_freeq_flush_nolock(Eina_FreeQ *fq)
+{
+ while ((fq->count > fq->count_max) || (fq->mem_total > fq->mem_max))
+ _eina_freeq_process(fq);
+}
+
+// ========================================================================= //
+
+EAPI Eina_FreeQ *
+eina_freeq_new(void)
+{
+ Eina_FreeQ *fq;
+
+ if (_eina_freeq_bypass == -1)
+ {
+ const char *s;
+
+ if (getenv("EINA_FREEQ_BYPASS")) _eina_freeq_bypass = 1;
+#ifdef HAVE_VALGRIND
+ else if (RUNNING_ON_VALGRIND) _eina_freeq_bypass = 1;
+#endif
+ else _eina_freeq_bypass = 0;
+ s = getenv("EINA_FREEQ_FILL_MAX");
+ if (s) _eina_freeq_fillpat_max = atoi(s);
+ s = getenv("EINA_FREEQ_TOTAL_MAX");
+ if (s) _eina_freeq_total_max = atoi(s);
+ s = getenv("EINA_FREEQ_MEM_MAX");
+ if (s) _eina_freeq_mem_max = atoi(s) * 1024;
+ }
+ fq = calloc(1, sizeof(Eina_FreeQ));
+ if (!fq) return NULL;
+ eina_lock_recursive_new(&(fq->lock));
+ fq->count_max = _eina_freeq_total_max;
+ fq->mem_max = _eina_freeq_mem_max;
+ return fq;
+}
+
+EAPI void
+eina_freeq_free(Eina_FreeQ *fq)
+{
+ if (!fq) return;
+ if (fq == _eina_freeq_main) _eina_freeq_main = NULL;
+ eina_freeq_clear(fq);
+ eina_lock_free(&(fq->lock));
+ free(fq);
+}
+
+EAPI void
+eina_freeq_main_set(Eina_FreeQ *fq)
+{
+ if (!fq) return;
+ _eina_freeq_main = fq;
+}
+
+EAPI Eina_FreeQ *
+eina_freeq_main_get(void)
+{
+ return _eina_freeq_main;
+}
+
+EAPI void
+eina_freeq_count_max_set(Eina_FreeQ *fq, int count)
+{
+ if (!fq) return;
+ if (count < 0) count = 0;
+ eina_lock_take(&(fq->lock));
+ fq->count_max = count;
+ _eina_freeq_flush_nolock(fq);
+ eina_lock_release(&(fq->lock));
+}
+
+EAPI int
+eina_freeq_count_max_get(Eina_FreeQ *fq)
+{
+ int count;
+
+ if (!fq) return 0;
+ eina_lock_take(&(fq->lock));
+ count = fq->count_max;
+ eina_lock_release(&(fq->lock));
+ return count;
+}
+
+EAPI void
+eina_freeq_mem_max_set(Eina_FreeQ *fq, size_t mem)
+{
+ if (!fq) return;
+ eina_lock_take(&(fq->lock));
+ fq->mem_max = mem;
+ _eina_freeq_flush_nolock(fq);
+ eina_lock_release(&(fq->lock));
+}
+
+EAPI size_t
+eina_freeq_mem_max_get(Eina_FreeQ *fq)
+{
+ size_t mem;
+
+ if (!fq) return 0;
+ eina_lock_take(&(fq->lock));
+ mem = fq->mem_max;
+ eina_lock_release(&(fq->lock));
+ return mem;
+}
+
+EAPI void
+eina_freeq_clear(Eina_FreeQ *fq)
+{
+ if (!fq) return;
+ eina_lock_take(&(fq->lock));
+ while (fq->count > 0) _eina_freeq_process(fq);
+ eina_lock_release(&(fq->lock));
+}
+
+EAPI void
+eina_freeq_reduce(Eina_FreeQ *fq, int count)
+{
+ if (!fq) return;
+ eina_lock_take(&(fq->lock));
+ while ((fq->count > 0) && (count > 0))
+ {
+ _eina_freeq_process(fq);
+ count--;
+ }
+ eina_lock_release(&(fq->lock));
+}
+
+EAPI Eina_Bool
+eina_freeq_ptr_pending(Eina_FreeQ *fq)
+{
+ Eina_Bool pending;
+
+ if (!fq) return EINA_FALSE;
+ eina_lock_take(&(fq->lock));
+ if (fq->blocks) pending = EINA_TRUE;
+ else pending = EINA_FALSE;
+ eina_lock_release(&(fq->lock));
+ return pending;
+}
+
+EAPI void
+eina_freeq_ptr_add(Eina_FreeQ *fq,
+ void *ptr,
+ void (*free_func) (void *ptr),
+ size_t size)
+{
+ Eina_FreeQ_Block *fb;
+
+ if (!ptr) return;
+ if (!free_func) free_func = free;
+ if ((size < _eina_freeq_fillpat_max) && (size > 0))
+ _eina_freeq_fill_do(ptr, size);
+ if ((!fq) || (_eina_freeq_bypass)) goto insta_free;
+ eina_lock_take(&(fq->lock));
+ if ((!fq->block_last) || (fq->block_last->end == ITEM_BLOCK_COUNT))
+ goto newblock;
+newblock_done:
+ fb = fq->block_last;
+ fb->items[fb->end].ptr = ptr;
+ fb->items[fb->end].free_func = free_func;
+ fb->items[fb->end].size = size;
+ fb->end++;
+ fq->count++;
+ fq->mem_total += size;
+ _eina_freeq_flush_nolock(fq);
+ eina_lock_release(&(fq->lock));
+ return;
+newblock:
+ if (!_eina_freeq_block_append(fq))
+ {
+ eina_lock_release(&(fq->lock));
+insta_free:
+ _eina_freeq_free_do(ptr, free_func, size);
+ return;
+ }
+ goto newblock_done;
+}
diff --git a/src/lib/eina/eina_freeq.h b/src/lib/eina/eina_freeq.h
new file mode 100644
index 0000000000..db3d0476f5
--- /dev/null
+++ b/src/lib/eina/eina_freeq.h
@@ -0,0 +1,276 @@
+#ifndef EINA_FREEQ_H_
+#define EINA_FREEQ_H_
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_FreeQ_Group Free Queue Group
+ * @ingroup Eina
+ *
+ * @brief This provides a mechanism to defer actual freeing of memory
+ * data at some time in the future. The main free queue will be driven
+ * by the EFL main loop and ensure data is eventually freed.
+ *
+ * For debugging and tuning you may set the following envrionment variables:
+ *
+ * EINA_FREEQ_BYPASS=1
+ *
+ * Set this environment variable to immediately bypass the free queue and
+ * have all items submitted free with their free function immediately.
+ *
+ * EINA_FREEQ_FILL_MAX=N
+ *
+ * This sets the maximum number of bytes to N an item in the free queue may
+ * be in size for the free queue to fill it with debugging values like
+ * 0x55 in every byte, to ensure you can see what memory has been freed
+ * or not when debugging in tools like gdb.
+ *
+ * EINA_FREEQ_TOTAL_MAX=N
+ *
+ * This sets the maximum number of items allowed to N on a free queue by
+ * default before it starts emptying the free queue out tomake room.
+ *
+ * EINA_FREEQ_MEM_MAX=N
+ *
+ * This sets the maximum total number of Kb (Kilobytes) of memory allowed
+ * on a free queue by default to N Kb worth of data.
+ *
+ * @{
+ *
+ * @since 1.19
+ *
+ * @typedef Eina_FreeQ
+ *
+ * A queue of pointers to free in the future. You may create custom free
+ * queues of your own to defer freeing, use the main free queue where the
+ * mainloop will free thnigs as it iterates, or eina will free everything
+ * on shut down.
+ *
+ */
+typedef struct _Eina_FreeQ Eina_FreeQ;
+
+/**
+ * @brief Create a new free queue to defer freeing of data with
+ *
+ * @return A new free queue
+ * @since 1.19
+ */
+EAPI Eina_FreeQ *
+eina_freeq_new(void);
+
+/**
+ * @brief Free a free queue and anything that is queued in it.
+ *
+ * @param fq The free queue to free and clear.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_freeq_free(Eina_FreeQ *fq);
+
+/**
+ * @brief Set the main free queue driven by the EFL mainloop.
+ *
+ * @param fq The free queue to set as the main loop one.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_freeq_main_set(Eina_FreeQ *fq);
+
+/**
+ * @brief Get the main loop free queue.
+ *
+ * @return The main loop free queue.
+ *
+ * @since 1.19
+ */
+EAPI Eina_FreeQ *
+eina_freeq_main_get(void);
+
+/**
+ * @brief Set the maximum number of free pointers this queue is allowed
+ *
+ * @param fq The free queue to alter
+ * @param count The maximum number of items allowed (must be >= 0)
+ *
+ * This will alter the maximum number of pointers allowed in the given free
+ * queue. If more items are added to the free queue than are allowed,
+ * excess items will be freed to make room for the new items. If count is
+ * changed, then excess items may be cleaned out at the time this API is
+ * called.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_freeq_count_max_set(Eina_FreeQ *fq, int count);
+
+/**
+ * @brief Get the maximum number of free pointers this queue is allowed
+ *
+ * @param fq The free queue to query
+ * @return The maximum number of free items allowed
+ *
+ * @since 1.19
+ */
+EAPI int
+eina_freeq_count_max_get(Eina_FreeQ *fq);
+
+/**
+ * @brief Set the maximum amount of memory allowed
+ *
+ * @param fq The free queue to alter
+ * @param mem The maximum memory in bytes
+ *
+ * This will alter the maximum amount of memory allowed for pointers stored
+ * in the free queue. The size used is the size give, so items given that
+ * are 0 sized will not contribute to this limit. If items with a total
+ * memory footprint are added to the free queue, items will be cleaned out
+ * until the total is below this limit. Changing the limit may involve
+ * cleaning out excess items from the free queue until the total amount of
+ * memory used by items in the queue is below or at the limit.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_freeq_mem_max_set(Eina_FreeQ *fq, size_t mem);
+
+/**
+ * @brief Get the maximum amount of memory allowed
+ *
+ * @param fq The free queue to query
+ * @return The maximum amount of memory in bytes
+ *
+ * @since 1.19
+ */
+EAPI size_t
+eina_freeq_mem_max_get(Eina_FreeQ *fq);
+
+/**
+ * @brief Clear out all queued items to be freed by freeing them
+ *
+ * @param fq The free queue to clear
+ *
+ * This will free and thus remove all queued items from the free queue when
+ * this function is called. When it returns the free queue should be
+ * empty.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_freeq_clear(Eina_FreeQ *fq);
+
+/**
+ * @brief Reduce the number of items in the free queue by up to @p count
+ *
+ * @param fq The free queue to reduce in item count
+ * @param count The number of items to try and free
+ *
+ * This will attempt to free up to @p count items from the given free queue
+ * and thus reduce the amount of memory it is holding on to. This function
+ * will return once it has removed @p count items or there are no more items
+ * to remove from the queue.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_freeq_reduce(Eina_FreeQ *fq, int count);
+
+/**
+ * @brief Return if there are any items pending a free in the free queue
+ *
+ * @param fq The free queue to query
+ * @raturn EINA_TRUE if there are items to free, EINA_FALSE otherwise
+ *
+ * @since 1.19
+ */
+EAPI Eina_Bool
+eina_freeq_ptr_pending(Eina_FreeQ *fq);
+
+/**
+ * @brief Add a pointer with free function and size to the free queue
+ *
+ * @param fq The free queue to add the pointer to
+ * @param ptr The pointer to free
+ * @param free_func The function used to free the pointer with
+ * @param size The size of the data the pointer points to
+ *
+ * This adds the given @p ptr pointer to the queue to be freed later on.
+ * The function @p free_func will be used, or if this is NULL, it is assumed
+ * the libc free() function will be used then instead. The @p size parameter
+ * determines the size of the data pointed to, but if this is 0 then no
+ * assumptions are made about size and the pointer is considered opaque. A
+ * zero sized pointer will not contribute to the total memory usage of
+ * items in the queue as well. If @p size is supplied it must be correct
+ * as the memory may be written to for debugging purposes or otherwise
+ * inspected or checksummed. Once a pointer is added to the free queue
+ * with this API the memory should be considered freed as if the real
+ * @p free_func was called immediately (and it may actually be called
+ * immediately if certain environment variables are set). A free queue exists
+ * to move the cost of freeing to another point in time when it is more
+ * convenient to do so as well as provide some robustness for badly
+ * written code that may access memory after freeing. Note that when using
+ * tools like valgrind, eina detects this and will also immediately free
+ * the data so valgrind's own memory checkers can detect use after free
+ * as normal.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_freeq_ptr_add(Eina_FreeQ *fq, void *ptr, void (*free_func) (void *ptr), size_t size);
+
+/**
+ * @brief Add a pointer to the main free queue
+ *
+ * @param ptr The pointer to free
+ * @param free_func The function used to free the pointer with
+ * @param size The size of the data the pointer points to
+ *
+ * This is the same as eina_freeq_ptr_add() but the main free queue is
+ * fetched by eina_freeq_main_get().
+ *
+ * @since 1.19
+ */
+static inline void
+eina_freeq_ptr_main_add(void *ptr, void (*free_func) (void *ptr), size_t size)
+{
+ eina_freeq_ptr_add(eina_freeq_main_get(), ptr, free_func, size);
+}
+
+/**
+ * @brief Convenience macro for well known structures and types
+ *
+ * @param ptr The pointer to free
+ *
+ * This is the same as eina_freeq_ptr_main_add() but the free function is
+ * assumed to be the libc free() function, and size is provided by
+ * sizeof(*ptr), so it will not work on void pointers or will be inaccurate
+ * for pointers to arrays. For arrays please use EINA_FREEQ_ARRAY_FREE()
+ *
+ * @since 1.19
+ */
+#define EINA_FREEQ_FREE(ptr) eina_freeq_ptr_main_add(ptr, NULL, sizeof(*(ptr)))
+
+/**
+ * @brief Convenience macro for well known structures and types
+ *
+ * @param ptr The pointer to free
+ *
+ * This is the same as eina_freeq_ptr_main_add() but the free function is
+ * assumed to be the libc free() function, and size is provided by
+ * sizeof(*ptr), so it will not work on void pointers. Total size is multiplied
+ * by the count @p n so it should work well for arrays of types.
+ *
+ * @since 1.19
+ */
+#define EINA_FREEQ_N_FREE(ptr, n) eina_freeq_ptr_main_add(ptr, NULL, sizeof(*(ptr)) * n)
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index 118b340cbe..70bd7586e2 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -68,6 +68,7 @@
#include "eina_inarray.h"
#include "eina_value.h"
#include "eina_evlog.h"
+#include "eina_freeq.h"
/* no model for now
#include "eina_model.h"
*/
@@ -265,6 +266,7 @@ eina_init(void)
mtrace();
}
#endif
+ eina_freeq_main_set(eina_freeq_new());
if (!eina_log_init())
{
@@ -332,6 +334,7 @@ eina_shutdown(void)
#ifdef EINA_HAVE_DEBUG_THREADS
pthread_mutex_destroy(&_eina_tracking_lock);
#endif
+ eina_freeq_free(eina_freeq_main_get());
#ifdef MT
if (_mt_enabled)
{
diff --git a/src/tests/eina/eina_suite.c b/src/tests/eina/eina_suite.c
index 2c35a11536..7bdd62f742 100644
--- a/src/tests/eina/eina_suite.c
+++ b/src/tests/eina/eina_suite.c
@@ -81,6 +81,7 @@ static const Efl_Test_Case etc[] = {
{ "Bezier", eina_test_bezier },
{ "SafePointer", eina_test_safepointer },
{ "Slice", eina_test_slice },
+ { "Free Queue", eina_test_freeq },
{ NULL, NULL }
};
diff --git a/src/tests/eina/eina_suite.h b/src/tests/eina/eina_suite.h
index 3ff362e7bd..3bda471d1f 100644
--- a/src/tests/eina/eina_suite.h
+++ b/src/tests/eina/eina_suite.h
@@ -73,5 +73,6 @@ void eina_test_promise(TCase *tc);
void eina_test_bezier(TCase *tc);
void eina_test_safepointer(TCase *tc);
void eina_test_slice(TCase *tc);
+void eina_test_freeq(TCase *tc);
#endif /* EINA_SUITE_H_ */
diff --git a/src/tests/eina/eina_test_freeq.c b/src/tests/eina/eina_test_freeq.c
new file mode 100644
index 0000000000..bf732b9be0
--- /dev/null
+++ b/src/tests/eina/eina_test_freeq.c
@@ -0,0 +1,122 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <Eina.h>
+
+#include "eina_suite.h"
+
+START_TEST(freeq_simple)
+{
+ Eina_FreeQ *fq, *pfq;
+
+ eina_init();
+
+ fail_if(eina_freeq_main_get() == NULL);
+ pfq = eina_freeq_main_get();
+
+ fq = eina_freeq_new();
+ fail_if(!fq);
+
+ eina_freeq_main_set(fq);
+ fail_if(eina_freeq_main_get() != fq);
+
+ eina_freeq_free(fq);
+ fail_if(eina_freeq_main_get() != NULL);
+ eina_freeq_main_set(pfq);
+
+ eina_shutdown();
+}
+END_TEST
+
+static int _n = 0;
+
+static void freefn(void *data)
+{
+ free(data);
+ _n--;
+}
+
+START_TEST(freeq_tune)
+{
+ void *p;
+
+ eina_init();
+
+ eina_freeq_count_max_set(eina_freeq_main_get(), 3);
+ fail_if(eina_freeq_count_max_get(eina_freeq_main_get()) != 3);
+
+ eina_freeq_mem_max_set(eina_freeq_main_get(), 20);
+ fail_if(eina_freeq_mem_max_get(eina_freeq_main_get()) != 20);
+
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+ eina_freeq_ptr_main_add(NULL, freefn, 9);
+ fail_if(_n > 2);
+
+ eina_freeq_count_max_set(eina_freeq_main_get(), 1);
+ fail_if(_n > 1);
+
+ eina_freeq_clear(eina_freeq_main_get());
+ fail_if(_n > 0);
+
+ fail_if(eina_freeq_ptr_pending(eina_freeq_main_get()) == EINA_TRUE);
+
+ eina_shutdown();
+}
+END_TEST
+
+START_TEST(freeq_reduce)
+{
+ void *p;
+
+ eina_init();
+
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+
+ while (eina_freeq_ptr_pending(eina_freeq_main_get()))
+ eina_freeq_reduce(eina_freeq_main_get(), 1);
+ fail_if(_n > 0);
+ fail_if(eina_freeq_ptr_pending(eina_freeq_main_get()) == EINA_TRUE);
+
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+ _n++;
+ p = malloc(9);
+ eina_freeq_ptr_main_add(p, freefn, 9);
+
+ while (eina_freeq_ptr_pending(eina_freeq_main_get()))
+ eina_freeq_reduce(eina_freeq_main_get(), 5);
+ fail_if(_n > 0);
+ fail_if(eina_freeq_ptr_pending(eina_freeq_main_get()) == EINA_TRUE);
+
+ eina_shutdown();
+}
+END_TEST
+
+void
+eina_test_freeq(TCase *tc)
+{
+ tcase_add_test(tc, freeq_simple);
+ tcase_add_test(tc, freeq_tune);
+ tcase_add_test(tc, freeq_reduce);
+}