CmmLayoutStack: Add unwind information on stack fixups
[ghc.git] / rts / Pool.c
1 /* ---------------------------------------------------------------------------
2 *
3 * (c) The GHC Team, 2014-2015
4 *
5 * A pool of lazily allocated things
6 *
7 * --------------------------------------------------------------------------*/
8
9 #include "PosixSource.h"
10 #include "Rts.h"
11 #include "RtsUtils.h"
12 #include "Pool.h"
13
14 /* used to mark an entry as needing to be freed when released */
15 #define FLAG_SHOULD_FREE (1 << 0)
16
17 typedef struct PoolEntry_ {
18 struct PoolEntry_ *next;
19 void *thing;
20 StgWord flags;
21 } PoolEntry;
22
23 struct Pool_ {
24 /* the maximum number of allocated resources in the pool */
25 uint32_t max_size;
26 /* the number of allocated resources to keep in the pool when idle */
27 uint32_t desired_size;
28 /* how many things are currently allocated? (sum of lengths of available and
29 * taken lists) */
30 uint32_t current_size;
31 #ifdef THREADED_RTS
32 /* signaled when a thing is released */
33 Condition cond;
34 #endif
35 alloc_thing_fn alloc_fn;
36 free_thing_fn free_fn;
37
38 PoolEntry *available;
39 PoolEntry *taken;
40 #ifdef THREADED_RTS
41 /* protects entire data structure */
42 Mutex mutex;
43 #endif
44 };
45
46 Pool *poolInit(uint32_t max_size, uint32_t desired_size,
47 alloc_thing_fn alloc_fn, free_thing_fn free_fn) {
48 Pool *pool = stgMallocBytes(sizeof(Pool), "pool_init");
49 pool->max_size = max_size == 0 ? (uint32_t) -1 : max_size;
50 pool->desired_size = desired_size;
51 pool->current_size = 0;
52 pool->alloc_fn = alloc_fn;
53 pool->free_fn = free_fn;
54 pool->available = NULL;
55 pool->taken = NULL;
56 #ifdef THREADED_RTS
57 initMutex(&pool->mutex);
58 initCondition(&pool->cond);
59 #endif
60 return pool;
61 }
62
63 int poolFree(Pool *pool) {
64 if (pool->taken != NULL)
65 return 1;
66
67 poolSetMaxSize(pool, 0);
68 #ifdef THREADED_RTS
69 closeCondition(&pool->cond);
70 closeMutex(&pool->mutex);
71 #endif
72 free(pool);
73 return 0;
74 }
75
76 /* free available entries such that current_size <= size */
77 static void free_available(Pool *pool, uint32_t size) {
78 while (pool->current_size > size && pool->available != NULL) {
79 PoolEntry *ent = pool->available;
80 pool->free_fn(ent->thing);
81 pool->available = ent->next;
82 free(ent);
83 pool->current_size--;
84 }
85 }
86
87 void poolSetDesiredSize(Pool *pool, uint32_t size) {
88 ACQUIRE_LOCK(&pool->mutex);
89 pool->desired_size = size;
90 free_available(pool, size);
91 RELEASE_LOCK(&pool->mutex);
92 }
93
94 void poolSetMaxSize(Pool *pool, uint32_t size) {
95 ACQUIRE_LOCK(&pool->mutex);
96 if (size == 0)
97 size = (uint32_t) -1;
98 pool->max_size = size;
99 if (pool->desired_size > pool->max_size) {
100 pool->desired_size = size;
101 free_available(pool, size);
102 }
103 RELEASE_LOCK(&pool->mutex);
104 }
105
106 uint32_t poolGetMaxSize(Pool *pool) {
107 return pool->max_size;
108 }
109
110 uint32_t poolGetDesiredSize(Pool *pool) {
111 return pool->desired_size;
112 }
113
114 // Try taking a PoolEntry with an item from a pool,
115 // returning NULL if no items are available.
116 static PoolEntry *poolTryTake_(Pool *pool) {
117 PoolEntry *ent = NULL;
118 if (pool->available != NULL) {
119 ent = pool->available;
120 pool->available = ent->next;
121 } else if (pool->current_size < pool->max_size) {
122 ent = stgMallocBytes(sizeof(PoolEntry), "pool_take");
123 ent->flags = 0;
124 ent->thing = pool->alloc_fn();
125 pool->current_size++;
126 } else {
127 return NULL;
128 }
129
130 ent->next = pool->taken;
131 pool->taken = ent;
132 return ent;
133 }
134
135 void *poolTryTake(Pool *pool) {
136 ACQUIRE_LOCK(&pool->mutex);
137 PoolEntry *ent = poolTryTake_(pool);
138 RELEASE_LOCK(&pool->mutex);
139 return ent ? ent->thing : NULL;
140 }
141
142 void *poolTake(Pool *pool) {
143 PoolEntry *ent = NULL;
144 ACQUIRE_LOCK(&pool->mutex);
145 while (ent == NULL) {
146 ent = poolTryTake_(pool);
147 if (!ent) {
148 #ifdef THREADED_RTS
149 waitCondition(&pool->cond, &pool->mutex);
150 #else
151 barf("Tried to take from an empty pool");
152 #endif
153 }
154 }
155
156 RELEASE_LOCK(&pool->mutex);
157 return ent->thing;
158 }
159
160 void poolRelease(Pool *pool, void *thing) {
161 ACQUIRE_LOCK(&pool->mutex);
162 PoolEntry **last = &pool->taken;
163 PoolEntry *ent = pool->taken;
164 while (ent != NULL) {
165 if (ent->thing == thing) {
166 *last = ent->next;
167 if (pool->current_size > pool->desired_size
168 || ent->flags & FLAG_SHOULD_FREE) {
169 pool->free_fn(ent->thing);
170 free(ent);
171 } else {
172 ent->next = pool->available;
173 pool->available = ent;
174 #ifdef THREADED_RTS
175 signalCondition(&pool->cond);
176 #endif
177 }
178
179 RELEASE_LOCK(&pool->mutex);
180 return;
181 }
182
183 last = &ent->next;
184 ent = ent->next;
185 }
186
187 barf("pool_release: trying to release resource which doesn't belong to pool.");
188 }
189
190 void poolFlush(Pool *pool) {
191 ACQUIRE_LOCK(&pool->mutex);
192 free_available(pool, 0);
193 PoolEntry *ent = pool->taken;
194 while (ent != NULL) {
195 ent->flags |= FLAG_SHOULD_FREE;
196 ent = ent->next;
197 }
198 RELEASE_LOCK(&pool->mutex);
199 }