2017-12-20 19:53:09 +01:00
|
|
|
/*
|
2019-08-03 19:43:48 +02:00
|
|
|
* This software is licensed under the terms of the MIT License.
|
2017-12-20 19:53:09 +01:00
|
|
|
* See COPYING for further information.
|
|
|
|
* ---
|
2019-01-23 21:10:43 +01:00
|
|
|
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
2019-07-03 20:00:56 +02:00
|
|
|
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
2017-12-20 19:53:09 +01:00
|
|
|
*/
|
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"
|
|
|
|
|
2019-04-12 10:36:40 +02:00
|
|
|
typedef struct ObjHeader {
|
|
|
|
alignas(alignof(max_align_t)) struct ObjHeader *next;
|
|
|
|
} ObjHeader;
|
|
|
|
|
2017-12-13 20:05:12 +01:00
|
|
|
struct ObjectPool {
|
2018-01-12 19:26:07 +01:00
|
|
|
char *tag;
|
|
|
|
size_t size_of_object;
|
|
|
|
size_t max_objects;
|
2019-04-12 10:36:40 +02:00
|
|
|
#ifdef OBJPOOL_TRACK_STATS
|
2018-01-12 19:26:07 +01:00
|
|
|
size_t usage;
|
|
|
|
size_t peak_usage;
|
2019-04-12 10:36:40 +02:00
|
|
|
#endif
|
2018-01-12 19:26:07 +01:00
|
|
|
size_t num_extents;
|
|
|
|
char **extents;
|
2019-04-12 10:36:40 +02:00
|
|
|
ObjHeader *free_objects;
|
2018-01-12 19:26:07 +01:00
|
|
|
char objects[];
|
2017-12-13 20:05:12 +01:00
|
|
|
};
|
|
|
|
|
2019-08-03 22:42:58 +02:00
|
|
|
INLINE attr_returns_max_aligned
|
|
|
|
ObjHeader *obj_ptr(ObjectPool *pool, char *objects, size_t idx) {
|
2019-04-12 10:36:40 +02:00
|
|
|
return CASTPTR_ASSUME_ALIGNED(objects + idx * pool->size_of_object, ObjHeader);
|
2017-12-23 22:56:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void objpool_register_objects(ObjectPool *pool, char *objects) {
|
2018-01-12 19:26:07 +01:00
|
|
|
for(size_t i = 0; i < pool->max_objects; ++i) {
|
2019-04-12 10:36:40 +02:00
|
|
|
ObjHeader *o = obj_ptr(pool, objects, i);
|
|
|
|
o->next = pool->free_objects;
|
|
|
|
pool->free_objects = o;
|
2018-01-12 19:26:07 +01:00
|
|
|
}
|
2017-12-13 20:05:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ObjectPool *objpool_alloc(size_t obj_size, size_t max_objects, const char *tag) {
|
2018-01-12 19:26:07 +01:00
|
|
|
// TODO: overflow handling
|
|
|
|
|
2023-01-09 04:19:31 +01:00
|
|
|
auto pool = ALLOC_FLEX(ObjectPool, obj_size * max_objects);
|
2018-01-12 19:26:07 +01:00
|
|
|
pool->size_of_object = obj_size;
|
|
|
|
pool->max_objects = max_objects;
|
|
|
|
pool->tag = strdup(tag);
|
|
|
|
|
|
|
|
objpool_register_objects(pool, pool->objects);
|
|
|
|
|
|
|
|
log_debug("[%s] Allocated pool for %zu objects, %zu bytes each",
|
|
|
|
pool->tag,
|
|
|
|
pool->max_objects,
|
|
|
|
pool->size_of_object
|
|
|
|
);
|
|
|
|
|
|
|
|
return pool;
|
2017-12-13 20:05:12 +01:00
|
|
|
}
|
|
|
|
|
2017-12-23 22:56:14 +01:00
|
|
|
static char *objpool_add_extent(ObjectPool *pool) {
|
2023-01-09 04:19:31 +01:00
|
|
|
pool->extents = mem_realloc(pool->extents, (++pool->num_extents) * sizeof(*pool->extents));
|
|
|
|
char *extent = pool->extents[pool->num_extents - 1] = mem_alloc_array(pool->max_objects, pool->size_of_object);
|
2018-01-12 19:26:07 +01:00
|
|
|
objpool_register_objects(pool, extent);
|
|
|
|
return extent;
|
2017-12-23 22:56:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static char* objpool_fmt_size(ObjectPool *pool) {
|
2018-01-12 19:26:07 +01:00
|
|
|
switch(pool->num_extents) {
|
|
|
|
case 0:
|
|
|
|
return strfmt("%zu objects, %zu bytes each",
|
|
|
|
pool->max_objects,
|
|
|
|
pool->size_of_object
|
|
|
|
);
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
return strfmt("%zu objects, %zu bytes each, with 1 extent",
|
|
|
|
pool->max_objects * 2,
|
|
|
|
pool->size_of_object
|
|
|
|
);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return strfmt("%zu objects, %zu bytes each, with %zu extents",
|
|
|
|
pool->max_objects * (1 + pool->num_extents),
|
|
|
|
pool->size_of_object,
|
|
|
|
pool->num_extents
|
|
|
|
);
|
|
|
|
}
|
2017-12-23 22:56:14 +01:00
|
|
|
}
|
|
|
|
|
2019-04-12 10:36:40 +02:00
|
|
|
void *objpool_acquire(ObjectPool *pool) {
|
|
|
|
ObjHeader *obj = pool->free_objects;
|
2017-12-13 20:05:12 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if(obj) {
|
2017-12-23 22:56:14 +01:00
|
|
|
acquired:
|
2019-04-12 10:36:40 +02:00
|
|
|
pool->free_objects = obj->next;
|
2018-01-12 19:26:07 +01:00
|
|
|
memset(obj, 0, pool->size_of_object);
|
|
|
|
|
2019-04-12 10:36:40 +02:00
|
|
|
#ifdef OBJPOOL_TRACK_STATS
|
2018-01-12 19:26:07 +01:00
|
|
|
if(++pool->usage > pool->peak_usage) {
|
|
|
|
pool->peak_usage = pool->usage;
|
|
|
|
}
|
2019-04-12 10:36:40 +02:00
|
|
|
#endif
|
2018-01-12 19:26:07 +01:00
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *tmp = objpool_fmt_size(pool);
|
2019-04-25 01:43:18 +02:00
|
|
|
log_debug("[%s] Object pool exhausted (%s), extending",
|
2018-01-12 19:26:07 +01:00
|
|
|
pool->tag,
|
|
|
|
tmp
|
|
|
|
);
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(tmp);
|
2018-01-12 19:26:07 +01:00
|
|
|
|
|
|
|
objpool_add_extent(pool);
|
2019-04-12 10:36:40 +02:00
|
|
|
obj = pool->free_objects;
|
2018-01-12 19:26:07 +01:00
|
|
|
assert(obj != NULL);
|
|
|
|
goto acquired;
|
2017-12-13 20:05:12 +01:00
|
|
|
}
|
|
|
|
|
2019-04-12 10:36:40 +02:00
|
|
|
void objpool_release(ObjectPool *pool, void *object) {
|
2018-01-12 19:26:07 +01:00
|
|
|
objpool_memtest(pool, object);
|
2019-04-12 10:36:40 +02:00
|
|
|
ObjHeader *obj = object;
|
|
|
|
obj->next = pool->free_objects;
|
|
|
|
pool->free_objects = obj;
|
|
|
|
#ifdef OBJPOOL_TRACK_STATS
|
2018-01-12 19:26:07 +01:00
|
|
|
pool->usage--;
|
2019-04-12 10:36:40 +02:00
|
|
|
#endif
|
2017-12-13 20:05:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void objpool_free(ObjectPool *pool) {
|
2019-04-12 10:36:40 +02:00
|
|
|
#ifdef OBJPOOL_TRACK_STATS
|
2018-01-12 19:26:07 +01:00
|
|
|
if(pool->usage != 0) {
|
|
|
|
log_warn("[%s] %zu objects still in use", pool->tag, pool->usage);
|
|
|
|
}
|
2019-04-12 10:36:40 +02:00
|
|
|
#endif
|
2017-12-13 20:05:12 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
for(size_t i = 0; i < pool->num_extents; ++i) {
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(pool->extents[i]);
|
2018-01-12 19:26:07 +01:00
|
|
|
}
|
2017-12-13 20:05:12 +01:00
|
|
|
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(pool->extents);
|
|
|
|
mem_free(pool->tag);
|
|
|
|
mem_free(pool);
|
2017-12-13 20:05:12 +01:00
|
|
|
}
|
|
|
|
|
2017-12-23 22:56:14 +01:00
|
|
|
size_t objpool_object_size(ObjectPool *pool) {
|
2018-01-12 19:26:07 +01:00
|
|
|
return pool->size_of_object;
|
2017-12-23 22:56:14 +01:00
|
|
|
}
|
|
|
|
|
2017-12-13 20:05:12 +01:00
|
|
|
void objpool_get_stats(ObjectPool *pool, ObjectPoolStats *stats) {
|
2018-01-12 19:26:07 +01:00
|
|
|
stats->tag = pool->tag;
|
|
|
|
stats->capacity = pool->max_objects * (1 + pool->num_extents);
|
2019-04-12 10:36:40 +02:00
|
|
|
#ifdef OBJPOOL_TRACK_STATS
|
2018-01-12 19:26:07 +01:00
|
|
|
stats->usage = pool->usage;
|
|
|
|
stats->peak_usage = pool->peak_usage;
|
2019-04-12 10:36:40 +02:00
|
|
|
#else
|
|
|
|
stats->usage = 0;
|
|
|
|
stats->peak_usage = 0;
|
|
|
|
#endif
|
2017-12-13 20:05:12 +01:00
|
|
|
}
|
2017-12-23 22:56:14 +01:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
attr_unused
|
2019-04-12 10:36:40 +02:00
|
|
|
static bool objpool_object_in_subpool(ObjectPool *pool, ObjHeader *object, char *objects) {
|
2018-01-12 19:26:07 +01:00
|
|
|
char *objofs = (char*)object;
|
|
|
|
char *minofs = objects;
|
|
|
|
char *maxofs = objects + (pool->max_objects - 1) * pool->size_of_object;
|
2017-12-23 22:56:14 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if(objofs < minofs || objofs > maxofs) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-12-23 22:56:14 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
ptrdiff_t misalign = (ptrdiff_t)(objofs - objects) % pool->size_of_object;
|
2017-12-23 22:56:14 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if(misalign) {
|
|
|
|
log_fatal("[%s] Object pointer %p is misaligned by %zi",
|
|
|
|
pool->tag,
|
|
|
|
(void*)objofs,
|
|
|
|
(ssize_t)misalign
|
|
|
|
);
|
|
|
|
}
|
2017-12-23 22:56:14 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
return true;
|
2017-12-23 22:56:14 +01:00
|
|
|
}
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
attr_unused
|
2019-04-12 10:36:40 +02:00
|
|
|
static bool objpool_object_in_pool(ObjectPool *pool, ObjHeader *object) {
|
2018-01-12 19:26:07 +01:00
|
|
|
if(objpool_object_in_subpool(pool, object, pool->objects)) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-12-23 22:56:14 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
for(size_t i = 0; i < pool->num_extents; ++i) {
|
|
|
|
if(objpool_object_in_subpool(pool, object, pool->extents[i])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2017-12-23 22:56:14 +01:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
return false;
|
2017-12-23 22:56:14 +01:00
|
|
|
}
|
|
|
|
|
2019-04-12 10:36:40 +02:00
|
|
|
#ifdef OBJPOOL_DEBUG
|
|
|
|
void objpool_memtest(ObjectPool *pool, void *object) {
|
|
|
|
if(!objpool_object_in_pool(pool, object)) {
|
|
|
|
log_fatal("[%s] Object pointer %p does not belong to this pool",
|
|
|
|
pool->tag,
|
|
|
|
object
|
|
|
|
);
|
|
|
|
}
|
2017-12-23 22:56:14 +01:00
|
|
|
}
|
2019-04-12 10:36:40 +02:00
|
|
|
#endif
|