rts/Pool.c (159 lines of code) (raw):
/* ---------------------------------------------------------------------------
*
* (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 */
uint32_t max_size;
/* the number of allocated resources to keep in the pool when idle */
uint32_t desired_size;
/* how many things are currently allocated? (sum of lengths of available and
* taken lists) */
uint32_t current_size;
#if defined(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;
#if defined(THREADED_RTS)
/* protects entire data structure */
Mutex mutex;
#endif
};
Pool *poolInit(uint32_t max_size, uint32_t 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 ? (uint32_t) -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;
#if defined(THREADED_RTS)
initMutex(&pool->mutex);
initCondition(&pool->cond);
#endif
return pool;
}
int poolFree(Pool *pool) {
if (pool->taken != NULL)
return 1;
poolSetMaxSize(pool, 0);
#if defined(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, uint32_t 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, uint32_t size) {
ACQUIRE_LOCK(&pool->mutex);
pool->desired_size = size;
free_available(pool, size);
RELEASE_LOCK(&pool->mutex);
}
void poolSetMaxSize(Pool *pool, uint32_t size) {
ACQUIRE_LOCK(&pool->mutex);
if (size == 0)
size = (uint32_t) -1;
pool->max_size = size;
if (pool->desired_size > pool->max_size) {
pool->desired_size = size;
free_available(pool, size);
}
RELEASE_LOCK(&pool->mutex);
}
uint32_t poolGetMaxSize(Pool *pool) {
return pool->max_size;
}
uint32_t poolGetDesiredSize(Pool *pool) {
return pool->desired_size;
}
// Try taking a PoolEntry with an item from a pool,
// returning NULL if no items are available.
static PoolEntry *poolTryTake_(Pool *pool) {
PoolEntry *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 {
return NULL;
}
ent->next = pool->taken;
pool->taken = ent;
return ent;
}
void *poolTryTake(Pool *pool) {
ACQUIRE_LOCK(&pool->mutex);
PoolEntry *ent = poolTryTake_(pool);
RELEASE_LOCK(&pool->mutex);
return ent ? ent->thing : NULL;
}
void *poolTake(Pool *pool) {
PoolEntry *ent = NULL;
ACQUIRE_LOCK(&pool->mutex);
while (ent == NULL) {
ent = poolTryTake_(pool);
if (!ent) {
#if defined(THREADED_RTS)
waitCondition(&pool->cond, &pool->mutex);
#else
barf("Tried to take from an empty pool");
#endif
}
}
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;
#if defined(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);
}