summaryrefslogtreecommitdiff
path: root/rts/Pool.c
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2015-10-22 22:16:46 +0200
committerBen Gamari <ben@smart-cactus.org>2015-11-23 15:40:44 +0100
commita3a8ce6e60466cb3742506c7d7bfa1a5b1012728 (patch)
treec56c2c1a02cae391f6bb332a99c0e5284daade8c /rts/Pool.c
parent36b213903db2363c2153f93c78bce079083f3d68 (diff)
downloadhaskell-a3a8ce6e60466cb3742506c7d7bfa1a5b1012728.tar.gz
rts: Add simple resource pool
Diffstat (limited to 'rts/Pool.c')
-rw-r--r--rts/Pool.c180
1 files changed, 180 insertions, 0 deletions
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);
+}