2017-12-20 19:53:09 +01:00
|
|
|
/*
|
|
|
|
* This software is licensed under the terms of the MIT-License
|
|
|
|
* See COPYING for further information.
|
|
|
|
* ---
|
|
|
|
* Copyright (c) 2011-2017, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2017, Andrei Alexeyev <akari@alienslab.net>.
|
|
|
|
*/
|
2017-12-13 20:05:12 +01:00
|
|
|
|
2017-12-20 19:57:29 +01:00
|
|
|
#include "taisei.h"
|
|
|
|
|
2017-12-13 20:05:12 +01:00
|
|
|
#include "objectpool.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "list.h"
|
|
|
|
|
|
|
|
#ifdef OBJPOOL_DEBUG
|
|
|
|
#define OBJ_USED(pool,obj) ((pool)->usemap[((uintptr_t)(obj) - (uintptr_t)(pool)->objects) / (pool)->size_of_object])
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct ObjectPool {
|
|
|
|
char *tag;
|
|
|
|
size_t size_of_object;
|
|
|
|
size_t max_objects;
|
|
|
|
size_t usage;
|
|
|
|
size_t peak_usage;
|
|
|
|
ObjectInterface *free_objects;
|
|
|
|
|
|
|
|
IF_OBJPOOL_DEBUG(
|
|
|
|
bool *usemap;
|
|
|
|
)
|
|
|
|
|
|
|
|
char objects[];
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline ObjectInterface* obj_ptr(ObjectPool *pool, size_t idx) {
|
|
|
|
return (ObjectInterface*)(pool->objects + idx * pool->size_of_object);
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) {
|
|
|
|
// TODO: overflow handling
|
|
|
|
|
|
|
|
ObjectPool *pool = malloc(sizeof(ObjectPool) + (obj_size * max_objects));
|
|
|
|
pool->size_of_object = obj_size;
|
|
|
|
pool->max_objects = max_objects;
|
|
|
|
pool->free_objects = NULL;
|
|
|
|
pool->usage = 0;
|
|
|
|
pool->peak_usage = 0;
|
|
|
|
pool->tag = strdup(tag);
|
|
|
|
|
|
|
|
IF_OBJPOOL_DEBUG({
|
|
|
|
pool->usemap = calloc(max_objects, sizeof(bool));
|
|
|
|
})
|
|
|
|
|
|
|
|
memset(pool->objects, 0, obj_size * max_objects);
|
|
|
|
|
|
|
|
for(size_t i = 0; i < max_objects; ++i) {
|
|
|
|
list_push((List**)&pool->free_objects, (List*)obj_ptr(pool, i));
|
|
|
|
}
|
|
|
|
|
|
|
|
log_debug("[%s] Allocated pool for %zu objects, %zu bytes each",
|
|
|
|
pool->tag,
|
|
|
|
pool->max_objects,
|
|
|
|
pool->size_of_object
|
|
|
|
);
|
|
|
|
|
|
|
|
return pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjectInterface *objpool_acquire(ObjectPool *pool) {
|
|
|
|
ObjectInterface *obj = (ObjectInterface*)list_pop((List**)&pool->free_objects);
|
|
|
|
|
|
|
|
if(obj) {
|
|
|
|
memset(obj, 0, pool->size_of_object);
|
|
|
|
|
|
|
|
IF_OBJPOOL_DEBUG({
|
|
|
|
OBJ_USED(pool, obj) = true;
|
|
|
|
})
|
|
|
|
|
|
|
|
if(++pool->usage > pool->peak_usage) {
|
|
|
|
pool->peak_usage = pool->usage;
|
|
|
|
}
|
|
|
|
|
|
|
|
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_fatal("[%s] Object pool exhausted (%zu objects, %zu bytes each)",
|
|
|
|
pool->tag,
|
|
|
|
pool->max_objects,
|
|
|
|
pool->size_of_object
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void objpool_release(ObjectPool *pool, ObjectInterface *object) {
|
|
|
|
IF_OBJPOOL_DEBUG({
|
|
|
|
char *objofs = (char*)object;
|
|
|
|
char *minofs = pool->objects;
|
|
|
|
char *maxofs = pool->objects + (pool->max_objects - 1) * pool->size_of_object;
|
|
|
|
|
|
|
|
if(objofs < minofs || objofs > maxofs) {
|
|
|
|
log_fatal("[%s] Object pointer %p is not within range %p - %p",
|
|
|
|
pool->tag,
|
|
|
|
(void*)objofs,
|
|
|
|
(void*)minofs,
|
|
|
|
(void*)maxofs
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
ptrdiff_t misalign = (ptrdiff_t)(objofs - pool->objects) % pool->size_of_object;
|
|
|
|
|
|
|
|
if(misalign) {
|
|
|
|
log_fatal("[%s] Object pointer %p is misaligned by %zi",
|
|
|
|
pool->tag,
|
|
|
|
(void*)objofs,
|
|
|
|
(ssize_t)misalign
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!OBJ_USED(pool, object)) {
|
|
|
|
log_fatal("[%s] Attempted to release an unused object %p",
|
|
|
|
pool->tag,
|
|
|
|
(void*)objofs
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
OBJ_USED(pool, object) = false;
|
|
|
|
})
|
|
|
|
|
2017-12-14 04:05:01 +01:00
|
|
|
list_push((List**)&pool->free_objects, (List*)object);
|
2017-12-13 20:05:12 +01:00
|
|
|
pool->usage--;
|
|
|
|
// log_debug("[%s] Usage: %zu", pool->tag, pool->usage);
|
|
|
|
}
|
|
|
|
|
|
|
|
void objpool_free(ObjectPool *pool) {
|
|
|
|
if(!pool) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pool->usage != 0) {
|
|
|
|
log_warn("[%s] %zu objects still in use", pool->tag, pool->usage);
|
|
|
|
}
|
|
|
|
|
|
|
|
IF_OBJPOOL_DEBUG({
|
|
|
|
free(pool->usemap);
|
|
|
|
})
|
|
|
|
|
|
|
|
free(pool->tag);
|
|
|
|
free(pool);
|
|
|
|
}
|
|
|
|
|
|
|
|
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
|
|
|
|
stats->tag = pool->tag;
|
|
|
|
stats->capacity = pool->max_objects;
|
|
|
|
stats->usage = pool->usage;
|
|
|
|
stats->peak_usage = pool->peak_usage;
|
|
|
|
}
|