2016-11-04 03:06:15 -07:00
|
|
|
#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
|
|
|
|
{
|
2017-01-10 21:48:57 -08:00
|
|
|
Eina_Lock lock; // recursive lock, unused for postponed queues (thread-local)
|
2016-11-04 03:06:15 -07:00
|
|
|
int count; // number of item slots used
|
2017-01-10 21:48:57 -08:00
|
|
|
int count_max; // maximum number of slots allowed to be used or -1
|
2016-11-04 03:06:15 -07:00
|
|
|
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
|
2017-01-10 21:48:57 -08:00
|
|
|
Eina_Bool bypass; // 0 if not to bypass, 1 if we should bypass
|
|
|
|
Eina_Bool postponed; // 1 if postponed type of freeq (eg. temp strings)
|
|
|
|
Eina_Bool unlocked; // 0 by default, 1 if thread-local (lock not used)
|
2016-11-04 03:06:15 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// ========================================================================= //
|
|
|
|
|
2016-12-20 22:37:47 -08:00
|
|
|
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 unsigned char _eina_freeq_fillpat_freed_val = 0x77;
|
|
|
|
static int _eina_freeq_total_max = ITEM_TOTAL_MAX;
|
|
|
|
static size_t _eina_freeq_mem_max = ITEM_MEM_MAX;
|
2016-11-04 03:06:15 -07:00
|
|
|
|
|
|
|
// ========================================================================= //
|
|
|
|
|
2017-01-10 21:48:57 -08:00
|
|
|
#define LOCK_FQ(fq); do { \
|
|
|
|
if (!fq->unlocked) eina_lock_take(&(fq->lock)); } while(0)
|
|
|
|
#define UNLOCK_FQ(fq); do { \
|
|
|
|
if (!fq->unlocked) eina_lock_release(&(fq->lock)); } while(0)
|
|
|
|
|
|
|
|
// ========================================================================= //
|
|
|
|
|
2016-12-20 22:37:47 -08:00
|
|
|
static inline void
|
2016-11-04 03:06:15 -07:00
|
|
|
_eina_freeq_fill_do(void *ptr, size_t size)
|
|
|
|
{
|
|
|
|
if (ptr) memset(ptr, _eina_freeq_fillpat_val, size);
|
|
|
|
}
|
|
|
|
|
2016-12-20 22:37:47 -08:00
|
|
|
static inline void
|
|
|
|
_eina_freeq_freed_fill_do(void *ptr, size_t size)
|
|
|
|
{
|
|
|
|
if (_eina_freeq_fillpat_freed_val == 0) return;
|
|
|
|
if (ptr) memset(ptr, _eina_freeq_fillpat_freed_val, size);
|
|
|
|
}
|
|
|
|
|
2016-11-04 03:06:15 -07:00
|
|
|
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))
|
2016-12-20 22:37:47 -08:00
|
|
|
{
|
|
|
|
_eina_freeq_fill_check(ptr, free_func, size);
|
|
|
|
_eina_freeq_freed_fill_do(ptr, size);
|
|
|
|
}
|
2016-11-04 03:06:15 -07:00
|
|
|
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)
|
|
|
|
{
|
2017-01-10 21:48:57 -08:00
|
|
|
if (fq->postponed) return;
|
|
|
|
|
2016-11-04 03:06:15 -07:00
|
|
|
while ((fq->count > fq->count_max) || (fq->mem_total > fq->mem_max))
|
|
|
|
_eina_freeq_process(fq);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========================================================================= //
|
|
|
|
|
2017-01-10 21:48:57 -08:00
|
|
|
static Eina_FreeQ *
|
|
|
|
_eina_freeq_new_default(void)
|
2016-11-04 03:06:15 -07:00
|
|
|
{
|
|
|
|
Eina_FreeQ *fq;
|
|
|
|
|
2017-01-10 21:48:57 -08:00
|
|
|
if (EINA_UNLIKELY(_eina_freeq_bypass == -1))
|
2016-11-04 03:06:15 -07:00
|
|
|
{
|
|
|
|
const char *s;
|
2016-12-20 17:06:32 -08:00
|
|
|
int v;
|
|
|
|
|
|
|
|
s = getenv("EINA_FREEQ_BYPASS");
|
|
|
|
if (s)
|
|
|
|
{
|
|
|
|
v = atoi(s);
|
|
|
|
if (v == 0) _eina_freeq_bypass = 0;
|
|
|
|
else _eina_freeq_bypass = 1;
|
|
|
|
}
|
|
|
|
if (_eina_freeq_bypass == -1)
|
|
|
|
{
|
2016-11-04 03:06:15 -07:00
|
|
|
#ifdef HAVE_VALGRIND
|
2016-12-20 17:06:32 -08:00
|
|
|
if (RUNNING_ON_VALGRIND) _eina_freeq_bypass = 1;
|
|
|
|
else
|
2016-11-04 03:06:15 -07:00
|
|
|
#endif
|
2016-12-20 17:06:32 -08:00
|
|
|
_eina_freeq_bypass = 0;
|
|
|
|
}
|
2016-11-04 03:06:15 -07:00
|
|
|
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;
|
2016-12-20 22:37:47 -08:00
|
|
|
s = getenv("EINA_FREEQ_FILL");
|
|
|
|
if (s) _eina_freeq_fillpat_val = atoi(s);
|
|
|
|
s = getenv("EINA_FREEQ_FILL_FREED");
|
|
|
|
if (s) _eina_freeq_fillpat_freed_val = atoi(s);
|
2016-11-04 03:06:15 -07:00
|
|
|
}
|
|
|
|
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;
|
2016-11-09 22:10:41 -08:00
|
|
|
fq->bypass = _eina_freeq_bypass;
|
2016-11-04 03:06:15 -07:00
|
|
|
return fq;
|
|
|
|
}
|
|
|
|
|
2017-01-10 21:48:57 -08:00
|
|
|
static Eina_FreeQ *
|
|
|
|
_eina_freeq_new_postponed(void)
|
|
|
|
{
|
|
|
|
Eina_FreeQ *fq;
|
|
|
|
|
|
|
|
fq= calloc(1, sizeof(*fq));
|
|
|
|
if (!fq) return NULL;
|
|
|
|
fq->mem_max = 0;
|
|
|
|
fq->count_max = -1;
|
|
|
|
fq->postponed = EINA_TRUE;
|
|
|
|
fq->unlocked = EINA_TRUE;
|
|
|
|
return fq;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_FreeQ *
|
|
|
|
eina_freeq_new(Eina_FreeQ_Type type)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case EINA_FREEQ_DEFAULT:
|
|
|
|
return _eina_freeq_new_default();
|
|
|
|
case EINA_FREEQ_POSTPONED:
|
|
|
|
return _eina_freeq_new_postponed();
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-04 03:06:15 -07:00
|
|
|
EAPI void
|
|
|
|
eina_freeq_free(Eina_FreeQ *fq)
|
|
|
|
{
|
|
|
|
if (!fq) return;
|
|
|
|
if (fq == _eina_freeq_main) _eina_freeq_main = NULL;
|
|
|
|
eina_freeq_clear(fq);
|
2017-01-10 21:48:57 -08:00
|
|
|
if (!fq->unlocked) eina_lock_free(&(fq->lock));
|
2016-11-04 03:06:15 -07:00
|
|
|
free(fq);
|
|
|
|
}
|
|
|
|
|
2017-01-10 21:48:57 -08:00
|
|
|
EAPI Eina_FreeQ_Type
|
|
|
|
eina_freeq_type_get(Eina_FreeQ *fq)
|
|
|
|
{
|
|
|
|
if (fq && fq->postponed)
|
|
|
|
return EINA_FREEQ_POSTPONED;
|
|
|
|
return EINA_FREEQ_DEFAULT;
|
|
|
|
}
|
|
|
|
|
2017-01-16 21:07:45 -08:00
|
|
|
void
|
2016-11-04 03:06:15 -07:00
|
|
|
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;
|
2017-01-10 21:48:57 -08:00
|
|
|
if (fq->postponed) return;
|
|
|
|
if (count < 0) count = -1;
|
|
|
|
LOCK_FQ(fq);
|
2016-11-09 22:10:41 -08:00
|
|
|
fq->bypass = 0;
|
2016-11-04 03:06:15 -07:00
|
|
|
fq->count_max = count;
|
|
|
|
_eina_freeq_flush_nolock(fq);
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI int
|
|
|
|
eina_freeq_count_max_get(Eina_FreeQ *fq)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
|
|
|
|
if (!fq) return 0;
|
2017-01-10 21:48:57 -08:00
|
|
|
LOCK_FQ(fq);
|
2016-11-09 22:10:41 -08:00
|
|
|
if (fq->bypass) count = 0;
|
|
|
|
else count = fq->count_max;
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
|
|
|
eina_freeq_mem_max_set(Eina_FreeQ *fq, size_t mem)
|
|
|
|
{
|
|
|
|
if (!fq) return;
|
2017-01-10 21:48:57 -08:00
|
|
|
if (fq->postponed) return;
|
|
|
|
LOCK_FQ(fq);
|
2016-11-09 22:10:41 -08:00
|
|
|
fq->bypass = 0;
|
2016-11-04 03:06:15 -07:00
|
|
|
fq->mem_max = mem;
|
|
|
|
_eina_freeq_flush_nolock(fq);
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI size_t
|
|
|
|
eina_freeq_mem_max_get(Eina_FreeQ *fq)
|
|
|
|
{
|
|
|
|
size_t mem;
|
|
|
|
|
|
|
|
if (!fq) return 0;
|
2017-01-10 21:48:57 -08:00
|
|
|
LOCK_FQ(fq);
|
2016-11-09 22:10:41 -08:00
|
|
|
if (fq->bypass) mem = 0;
|
|
|
|
else mem = fq->mem_max;
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
|
|
|
eina_freeq_clear(Eina_FreeQ *fq)
|
|
|
|
{
|
|
|
|
if (!fq) return;
|
2017-01-10 21:48:57 -08:00
|
|
|
LOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
while (fq->count > 0) _eina_freeq_process(fq);
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
|
|
|
eina_freeq_reduce(Eina_FreeQ *fq, int count)
|
|
|
|
{
|
|
|
|
if (!fq) return;
|
2017-01-10 21:48:57 -08:00
|
|
|
LOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
while ((fq->count > 0) && (count > 0))
|
|
|
|
{
|
|
|
|
_eina_freeq_process(fq);
|
|
|
|
count--;
|
|
|
|
}
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_Bool
|
|
|
|
eina_freeq_ptr_pending(Eina_FreeQ *fq)
|
|
|
|
{
|
|
|
|
Eina_Bool pending;
|
|
|
|
|
|
|
|
if (!fq) return EINA_FALSE;
|
2017-01-10 21:48:57 -08:00
|
|
|
LOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
if (fq->blocks) pending = EINA_TRUE;
|
|
|
|
else pending = EINA_FALSE;
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
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;
|
2017-02-07 05:41:07 -08:00
|
|
|
if ((((fq) && !fq->postponed) || (!fq)) &&
|
|
|
|
(size < _eina_freeq_fillpat_max) && (size > 0))
|
2016-11-04 03:06:15 -07:00
|
|
|
_eina_freeq_fill_do(ptr, size);
|
2017-01-10 21:48:57 -08:00
|
|
|
|
|
|
|
if (!fq || fq->bypass)
|
|
|
|
{
|
|
|
|
_eina_freeq_free_do(ptr, free_func, size);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
if ((!fq->block_last) || (fq->block_last->end == ITEM_BLOCK_COUNT))
|
2017-01-10 21:48:57 -08:00
|
|
|
{
|
|
|
|
if (!_eina_freeq_block_append(fq))
|
|
|
|
{
|
|
|
|
UNLOCK_FQ(fq);
|
|
|
|
if (!fq->postponed)
|
|
|
|
_eina_freeq_free_do(ptr, free_func, size);
|
|
|
|
else
|
|
|
|
EINA_LOG_ERR("Could not add a pointer to the free queue! This "
|
|
|
|
"program will leak resources!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-04 03:06:15 -07:00
|
|
|
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);
|
2017-01-10 21:48:57 -08:00
|
|
|
UNLOCK_FQ(fq);
|
2016-11-04 03:06:15 -07:00
|
|
|
}
|