From a3a8ce6e60466cb3742506c7d7bfa1a5b1012728 Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Thu, 22 Oct 2015 22:16:46 +0200 Subject: rts: Add simple resource pool --- rts/Pool.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 rts/Pool.c (limited to 'rts/Pool.c') diff --git a/rts/Pool.c b/rts/Pool.c new file mode 100644 index 0000000000..6c238075ef --- /dev/null +++ b/rts/Pool.c @@ -0,0 +1,180 @@ +/* --------------------------------------------------------------------------- + * + * (c) The GHC Team, 2014-2015 + * + * A pool of lazily allocated things + * + * --------------------------------------------------------------------------*/ + +#include "PosixSource.h" +#include "Rts.h" +#include "RtsUtils.h" +#include "Pool.h" + +/* used to mark an entry as needing to be freed when released */ +#define FLAG_SHOULD_FREE (1 << 0) + +typedef struct PoolEntry_ { + struct PoolEntry_ *next; + void *thing; + StgWord flags; +} PoolEntry; + +struct Pool_ { + /* the maximum number of allocated resources in the pool */ + nat max_size; + /* the number of allocated resources to keep in the pool when idle */ + nat desired_size; + /* how many things are currently allocated? (sum of lengths of available and + * taken lists) */ + nat current_size; +#ifdef THREADED_RTS + /* signaled when a thing is released */ + Condition cond; +#endif + alloc_thing_fn alloc_fn; + free_thing_fn free_fn; + + PoolEntry *available; + PoolEntry *taken; +#ifdef THREADED_RTS + /* protects entire data structure */ + Mutex mutex; +#endif +}; + +Pool *poolInit(nat max_size, nat desired_size, + alloc_thing_fn alloc_fn, free_thing_fn free_fn) { + Pool *pool = stgMallocBytes(sizeof(Pool), "pool_init"); + pool->max_size = max_size == 0 ? (nat) -1 : max_size; + pool->desired_size = desired_size; + pool->current_size = 0; + pool->alloc_fn = alloc_fn; + pool->free_fn = free_fn; + pool->available = NULL; + pool->taken = NULL; +#ifdef THREADED_RTS + initMutex(&pool->mutex); + initCondition(&pool->cond); +#endif + return pool; +} + +int poolFree(Pool *pool) { + if (pool->taken != NULL) + return 1; + + poolSetMaxSize(pool, 0); +#ifdef THREADED_RTS + closeCondition(&pool->cond); + closeMutex(&pool->mutex); +#endif + free(pool); + return 0; +} + +/* free available entries such that current_size <= size */ +static void free_available(Pool *pool, nat size) { + while (pool->current_size > size && pool->available != NULL) { + PoolEntry *ent = pool->available; + pool->free_fn(ent->thing); + pool->available = ent->next; + free(ent); + pool->current_size--; + } +} + +void poolSetDesiredSize(Pool *pool, nat size) { + ACQUIRE_LOCK(&pool->mutex); + pool->desired_size = size; + free_available(pool, size); + RELEASE_LOCK(&pool->mutex); +} + +void poolSetMaxSize(Pool *pool, nat size) { + ACQUIRE_LOCK(&pool->mutex); + if (size == 0) + size = (nat) -1; + pool->max_size = size; + if (pool->desired_size > pool->max_size) { + pool->desired_size = size; + free_available(pool, size); + } + RELEASE_LOCK(&pool->mutex); +} + +nat poolGetMaxSize(Pool *pool) { + return pool->max_size; +} + +nat poolGetDesiredSize(Pool *pool) { + return pool->desired_size; +} + +void *poolTake(Pool *pool) { + PoolEntry *ent = NULL; + ACQUIRE_LOCK(&pool->mutex); + while (ent == NULL) { + if (pool->available != NULL) { + ent = pool->available; + pool->available = ent->next; + } else if (pool->current_size < pool->max_size) { + ent = stgMallocBytes(sizeof(PoolEntry), "pool_take"); + ent->flags = 0; + ent->thing = pool->alloc_fn(); + pool->current_size++; + } else { +#ifdef THREADED_RTS + waitCondition(&pool->cond, &pool->mutex); +#else + barf("Tried to take from an empty pool"); +#endif + } + } + + ent->next = pool->taken; + pool->taken = ent; + RELEASE_LOCK(&pool->mutex); + return ent->thing; +} + +void poolRelease(Pool *pool, void *thing) { + ACQUIRE_LOCK(&pool->mutex); + PoolEntry **last = &pool->taken; + PoolEntry *ent = pool->taken; + while (ent != NULL) { + if (ent->thing == thing) { + *last = ent->next; + if (pool->current_size > pool->desired_size + || ent->flags & FLAG_SHOULD_FREE) { + pool->free_fn(ent->thing); + free(ent); + } else { + ent->next = pool->available; + pool->available = ent; +#ifdef THREADED_RTS + signalCondition(&pool->cond); +#endif + } + + RELEASE_LOCK(&pool->mutex); + return; + } + + last = &ent->next; + ent = ent->next; + } + + barf("pool_release: trying to release resource which doesn't belong to pool."); +} + +void poolFlush(Pool *pool) { + ACQUIRE_LOCK(&pool->mutex); + free_available(pool, 0); + PoolEntry *ent = pool->taken; + while (ent != NULL) { + ent->flags |= FLAG_SHOULD_FREE; + ent = ent->next; + } + RELEASE_LOCK(&pool->mutex); +} -- cgit v1.2.1