#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include "libdwP.h"
#include "system.h"
#include "atomics.h"
#if USE_VG_ANNOTATIONS == 1
#include <helgrind.h>
#else
#define ANNOTATE_HAPPENS_BEFORE(X)
#define ANNOTATE_HAPPENS_AFTER(X)
#endif
#define THREAD_ID_UNSET ((size_t) -1)
static __thread size_t thread_id = THREAD_ID_UNSET;
static atomic_size_t next_id = ATOMIC_VAR_INIT(0);
struct libdw_memblock *
__libdw_alloc_tail (Dwarf *dbg)
{
if (thread_id == THREAD_ID_UNSET)
thread_id = atomic_fetch_add (&next_id, 1);
pthread_rwlock_rdlock (&dbg->mem_rwl);
if (thread_id >= dbg->mem_stacks)
{
pthread_rwlock_unlock (&dbg->mem_rwl);
pthread_rwlock_wrlock (&dbg->mem_rwl);
if (thread_id >= dbg->mem_stacks)
{
dbg->mem_tails = realloc (dbg->mem_tails, (thread_id+1)
* sizeof (struct libdw_memblock *));
if (dbg->mem_tails == NULL)
{
pthread_rwlock_unlock (&dbg->mem_rwl);
dbg->oom_handler();
}
for (size_t i = dbg->mem_stacks; i <= thread_id; i++)
dbg->mem_tails[i] = NULL;
dbg->mem_stacks = thread_id + 1;
ANNOTATE_HAPPENS_BEFORE (&dbg->mem_tails);
}
pthread_rwlock_unlock (&dbg->mem_rwl);
pthread_rwlock_rdlock (&dbg->mem_rwl);
}
ANNOTATE_HAPPENS_AFTER (&dbg->mem_tails);
struct libdw_memblock *result = dbg->mem_tails[thread_id];
if (result == NULL)
{
result = malloc (dbg->mem_default_size);
if (result == NULL)
{
pthread_rwlock_unlock (&dbg->mem_rwl);
dbg->oom_handler();
}
result->size = dbg->mem_default_size
- offsetof (struct libdw_memblock, mem);
result->remaining = result->size;
result->prev = NULL;
dbg->mem_tails[thread_id] = result;
}
pthread_rwlock_unlock (&dbg->mem_rwl);
return result;
}
struct libdw_memblock *
__libdw_thread_tail (Dwarf *dbg)
{
struct libdw_memblock *result;
pthread_rwlock_rdlock (&dbg->mem_rwl);
result = dbg->mem_tails[thread_id];
pthread_rwlock_unlock (&dbg->mem_rwl);
return result;
}
void *
__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
{
size_t size = MAX (dbg->mem_default_size,
(align - 1 +
2 * minsize + offsetof (struct libdw_memblock, mem)));
struct libdw_memblock *newp = malloc (size);
if (newp == NULL)
dbg->oom_handler ();
uintptr_t result = ((uintptr_t) newp->mem + align - 1) & ~(align - 1);
newp->size = size - offsetof (struct libdw_memblock, mem);
newp->remaining = (uintptr_t) newp + size - (result + minsize);
pthread_rwlock_rdlock (&dbg->mem_rwl);
newp->prev = dbg->mem_tails[thread_id];
dbg->mem_tails[thread_id] = newp;
pthread_rwlock_unlock (&dbg->mem_rwl);
return (void *) result;
}
Dwarf_OOM
dwarf_new_oom_handler (Dwarf *dbg, Dwarf_OOM handler)
{
Dwarf_OOM old = dbg->oom_handler;
dbg->oom_handler = handler;
return old;
}
void
__attribute ((noreturn)) attribute_hidden
__libdw_oom (void)
{
while (1)
error (EXIT_FAILURE, ENOMEM, "libdw");
}