/* --------------------------------------------------------------------------- * * (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); }