#include "libunwind_i.h"
#include <stdalign.h>
#include <stdatomic.h>
#ifdef __BIGGEST_ALIGNMENT__
# define MAX_ALIGN __BIGGEST_ALIGNMENT__
#else
# define MAX_ALIGN_(n) (n < 8 ? 8 : \
n < 16 ? 16 : n)
# define MAX_ALIGN MAX_ALIGN_(sizeof (long double))
#endif
static alignas(MAX_ALIGN) char sos_memory[SOS_MEMORY_SIZE];
static _Atomic size_t sos_memory_freepos = 0;
HIDDEN void *
sos_alloc (size_t size)
{
size_t pos;
size = UNW_ALIGN(size, MAX_ALIGN);
assert(((uintptr_t) &sos_memory[0] & (MAX_ALIGN-1)) == 0);
pos = atomic_fetch_add (&sos_memory_freepos, size);
assert (((uintptr_t) &sos_memory[pos] & (MAX_ALIGN-1)) == 0);
assert ((pos+size) <= SOS_MEMORY_SIZE);
return &sos_memory[pos];
}
static void
free_object (struct mempool *pool, void *object)
{
struct object *obj = object;
obj->next = pool->free_list;
pool->free_list = obj;
++pool->num_free;
}
static void
add_memory (struct mempool *pool, char *mem, size_t size, size_t obj_size)
{
char *obj;
for (obj = mem; obj <= mem + size - obj_size; obj += obj_size)
free_object (pool, obj);
}
static void
expand (struct mempool *pool)
{
size_t size;
char *mem;
size = pool->chunk_size;
GET_MEMORY (mem, size);
if (!mem)
{
size = UNW_ALIGN(pool->obj_size, unw_page_size);
GET_MEMORY (mem, size);
if (!mem)
{
size = pool->obj_size;
mem = sos_alloc (size);
}
}
add_memory (pool, mem, size, pool->obj_size);
}
HIDDEN void
mempool_init (struct mempool *pool, size_t obj_size, size_t reserve)
{
memset (pool, 0, sizeof (*pool));
lock_init (&pool->lock);
obj_size = UNW_ALIGN(obj_size, MAX_ALIGN);
if (!reserve)
{
reserve = unw_page_size / obj_size / 4;
if (!reserve)
reserve = 16;
}
pool->obj_size = obj_size;
pool->reserve = reserve;
pool->chunk_size = UNW_ALIGN(2*reserve*obj_size, unw_page_size);
expand (pool);
}
HIDDEN void *
mempool_alloc (struct mempool *pool)
{
intrmask_t saved_mask;
struct object *obj;
lock_acquire (&pool->lock, saved_mask);
{
if (pool->num_free <= pool->reserve)
expand (pool);
assert (pool->num_free > 0);
--pool->num_free;
obj = pool->free_list;
pool->free_list = obj->next;
}
lock_release (&pool->lock, saved_mask);
return obj;
}
HIDDEN void
mempool_free (struct mempool *pool, void *object)
{
intrmask_t saved_mask;
lock_acquire (&pool->lock, saved_mask);
{
free_object (pool, object);
}
lock_release (&pool->lock, saved_mask);
}