/*
* Pool allocator
* Copyright (C) 2008 Andreas Ă–man
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "arch/halloc.h"
#include "arch/threads.h"
#include "queue.h"
#include "showtime.h"
#include "pool.h"
LIST_HEAD(pool_segment_list, pool_segment);
/**
*
*/
typedef struct pool_item_dbg {
const char *file;
intptr_t line;
} pool_item_dbg_t;
/**
*
*/
typedef struct pool_item {
struct pool_item *link;
} pool_item_t;
/**
*
*/
typedef struct pool_segment {
LIST_ENTRY(pool_segment) ps_link;
void *ps_addr;
size_t ps_size;
#ifdef POOL_DEBUG
uint8_t *ps_mark;
#endif
} pool_segment_t;
/**
*
*/
struct pool {
struct pool_segment_list p_segments;
size_t p_item_size;
int p_flags;
hts_mutex_t p_mutex;
pool_item_t *p_item;
int p_num_out;
char *p_name;
};
/**
*
*/
static void
pool_segment_create(pool_t *p)
{
pool_segment_t *ps = malloc(sizeof(pool_segment_t));
size_t i;
pool_item_t *pi, *prev = NULL;
ps->ps_size = 65536;
ps->ps_addr = halloc(ps->ps_size);
for(i = 0; i <= ps->ps_size - p->p_item_size; i += p->p_item_size) {
pi = ps->ps_addr + i;
pi->link = prev;
prev = pi;
}
LIST_INSERT_HEAD(&p->p_segments, ps, ps_link);
p->p_item = pi;
}
#define ROUND_UP(p, round) ((p + round - 1) & ~(round - 1))
/**
*
*/
pool_t *
pool_create(const char *name, size_t item_size, int flags)
{
pool_t *p = calloc(1, sizeof(pool_t));
item_size = ROUND_UP(item_size, 8);
p->p_name = strdup(name);
#ifdef POOL_DEBUG
item_size += sizeof(pool_item_dbg_t);
#endif
p->p_item_size = item_size;
p->p_flags = flags;
if(flags & POOL_REENTRANT)
hts_mutex_init(&p->p_mutex);
return p;
}
/**
*
*/
void
pool_destroy(pool_t *p)
{
pool_segment_t *ps;
#ifdef POOL_DEBUG
if(1) {
pool_item_t *pi;
LIST_FOREACH(ps, &p->p_segments, ps_link) {
ps->ps_mark = malloc(ps->ps_size / p->p_item_size);
memset(ps->ps_mark, 0xff, ps->ps_size / p->p_item_size);
}
for(pi = p->p_item; pi != NULL; pi = pi->link) {
LIST_FOREACH(ps, &p->p_segments, ps_link) {
if((intptr_t)pi >= (intptr_t)ps->ps_addr &&
(intptr_t)pi < (intptr_t)ps->ps_addr + ps->ps_size) {
size_t off = ((void *)pi - ps->ps_addr) / p->p_item_size;
ps->ps_mark[off] = 0;
}
}
}
LIST_FOREACH(ps, &p->p_segments, ps_link) {
int items = ps->ps_size / p->p_item_size;
int i;
for(i = 0; i < items; i++) {
if(ps->ps_mark[i]) {
#ifdef POOL_DEBUG
pool_item_dbg_t *pid = ps->ps_addr + i * p->p_item_size;
printf("Leak at %p (%s:%d)\n",
pid, pid->file, (int)pid->line);
#else
printf("Leak at %p\n",
ps->ps_addr + i * p->p_item_size);
#endif
}
}
}
}
#endif
while((ps = LIST_FIRST(&p->p_segments)) != NULL) {
hfree(ps->ps_addr, ps->ps_size);
LIST_REMOVE(ps, ps_link);
#ifdef POOL_DEBUG
free(ps->ps_mark);
#endif
free(ps);
}
if(p->p_num_out)
TRACE(TRACE_INFO, "pool", "Destroying pool '%s', %d items out",
p->p_name, p->p_num_out);
if(p->p_flags & POOL_REENTRANT)
hts_mutex_destroy(&p->p_mutex);
free(p->p_name);
free(p);
}
/**
*
*/
void *
#ifdef POOL_DEBUG
pool_get_ex(pool_t *p, const char *file, int line)
#else
pool_get(pool_t *p)
#endif
{
if(p->p_flags & POOL_REENTRANT)
hts_mutex_lock(&p->p_mutex);
pool_item_t *pi = p->p_item;
if(pi == NULL) {
pool_segment_create(p);
pi = p->p_item;
}
p->p_item = pi->link;
p->p_num_out++;
if(p->p_flags & POOL_REENTRANT)
hts_mutex_unlock(&p->p_mutex);
if(p->p_flags & POOL_ZERO_MEM)
memset(pi, 0, p->p_item_size);
#ifdef POOL_DEBUG
pool_item_dbg_t *pid = (void *)pi;
pid->file = file;
pid->line = line;
return (void *)pi + sizeof(pool_item_dbg_t);
#else
return pi;
#endif
}
/**
*
*/
void
pool_put(pool_t *p, void *ptr)
{
#ifdef POOL_DEBUG
pool_item_t *pi = ptr - sizeof(pool_item_dbg_t);
#else
pool_item_t *pi = ptr;
#endif
#ifdef POOL_DEBUG
pool_segment_t *ps;
LIST_FOREACH(ps, &p->p_segments, ps_link)
if((intptr_t)pi >= (intptr_t)ps->ps_addr &&
(intptr_t)pi < (intptr_t)ps->ps_addr + ps->ps_size)
break;
assert(ps != NULL);
memset(pi, 0xff, p->p_item_size);
#endif
if(p->p_flags & POOL_REENTRANT)
hts_mutex_lock(&p->p_mutex);
pi->link = p->p_item;
p->p_item = pi;
p->p_num_out--;
if(p->p_flags & POOL_REENTRANT)
hts_mutex_unlock(&p->p_mutex);
}
/**
*
*/
int
pool_num(pool_t *p)
{
return p->p_num_out;
}