2 * UCW Library -- Memory Pools (One-Time Allocation)
4 * (c) 1997--2001 Martin Mares <mj@ucw.cz>
5 * (c) 2007 Pavel Charvat <pchar@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
14 #include "ucw/mempool.h"
18 #define MP_CHUNK_TAIL ALIGN_TO(sizeof(struct mempool_chunk), CPU_STRUCT_ALIGN)
19 #define MP_SIZE_MAX (~0U - MP_CHUNK_TAIL - CPU_PAGE_SIZE)
21 struct mempool_chunk {
22 struct mempool_chunk *next;
27 mp_align_size(uns size)
30 return ALIGN_TO(size + MP_CHUNK_TAIL, CPU_PAGE_SIZE) - MP_CHUNK_TAIL;
32 return ALIGN_TO(size, CPU_STRUCT_ALIGN);
37 mp_init(struct mempool *pool, uns chunk_size)
39 chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
40 *pool = (struct mempool) {
41 .chunk_size = chunk_size,
42 .threshold = chunk_size >> 1,
43 .last_big = &pool->last_big };
47 mp_new_big_chunk(uns size)
49 struct mempool_chunk *chunk;
50 chunk = xmalloc(size + MP_CHUNK_TAIL) + size;
56 mp_free_big_chunk(struct mempool_chunk *chunk)
58 xfree((void *)chunk - chunk->size);
62 mp_new_chunk(uns size)
65 struct mempool_chunk *chunk;
66 chunk = page_alloc(size + MP_CHUNK_TAIL) + size;
70 return mp_new_big_chunk(size);
75 mp_free_chunk(struct mempool_chunk *chunk)
78 page_free((void *)chunk - chunk->size, chunk->size + MP_CHUNK_TAIL);
80 mp_free_big_chunk(chunk);
85 mp_new(uns chunk_size)
87 chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
88 struct mempool_chunk *chunk = mp_new_chunk(chunk_size);
89 struct mempool *pool = (void *)chunk - chunk_size;
90 DBG("Creating mempool %p with %u bytes long chunks", pool, chunk_size);
92 *pool = (struct mempool) {
93 .state = { .free = { chunk_size - sizeof(*pool) }, .last = { chunk } },
94 .chunk_size = chunk_size,
95 .threshold = chunk_size >> 1,
96 .last_big = &pool->last_big };
101 mp_free_chain(struct mempool_chunk *chunk)
105 struct mempool_chunk *next = chunk->next;
106 mp_free_chunk(chunk);
112 mp_free_big_chain(struct mempool_chunk *chunk)
116 struct mempool_chunk *next = chunk->next;
117 mp_free_big_chunk(chunk);
123 mp_delete(struct mempool *pool)
125 DBG("Deleting mempool %p", pool);
126 mp_free_big_chain(pool->state.last[1]);
127 mp_free_chain(pool->unused);
128 mp_free_chain(pool->state.last[0]); // can contain the mempool structure
132 mp_flush(struct mempool *pool)
134 mp_free_big_chain(pool->state.last[1]);
135 struct mempool_chunk *chunk, *next;
136 for (chunk = pool->state.last[0]; chunk && (void *)chunk - chunk->size != pool; chunk = next)
139 chunk->next = pool->unused;
140 pool->unused = chunk;
142 pool->state.last[0] = chunk;
143 pool->state.free[0] = chunk ? chunk->size - sizeof(*pool) : 0;
144 pool->state.last[1] = NULL;
145 pool->state.free[1] = 0;
146 pool->state.next = NULL;
147 pool->last_big = &pool->last_big;
151 mp_stats_chain(struct mempool_chunk *chunk, struct mempool_stats *stats, uns idx)
155 stats->chain_size[idx] += chunk->size + sizeof(*chunk);
156 stats->chain_count[idx]++;
159 stats->total_size += stats->chain_size[idx];
163 mp_stats(struct mempool *pool, struct mempool_stats *stats)
165 bzero(stats, sizeof(*stats));
166 mp_stats_chain(pool->state.last[0], stats, 0);
167 mp_stats_chain(pool->state.last[1], stats, 1);
168 mp_stats_chain(pool->unused, stats, 2);
172 mp_total_size(struct mempool *pool)
174 struct mempool_stats stats;
175 mp_stats(pool, &stats);
176 return stats.total_size;
180 mp_alloc_internal(struct mempool *pool, uns size)
182 struct mempool_chunk *chunk;
183 if (size <= pool->threshold)
188 chunk = pool->unused;
189 pool->unused = chunk->next;
192 chunk = mp_new_chunk(pool->chunk_size);
193 chunk->next = pool->state.last[0];
194 pool->state.last[0] = chunk;
195 pool->state.free[0] = pool->chunk_size - size;
196 return (void *)chunk - pool->chunk_size;
198 else if (likely(size <= MP_SIZE_MAX))
201 uns aligned = ALIGN_TO(size, CPU_STRUCT_ALIGN);
202 chunk = mp_new_big_chunk(aligned);
203 chunk->next = pool->state.last[1];
204 pool->state.last[1] = chunk;
205 pool->state.free[1] = aligned - size;
206 return pool->last_big = (void *)chunk - aligned;
209 die("Cannot allocate %u bytes from a mempool", size);
213 mp_alloc(struct mempool *pool, uns size)
215 return mp_alloc_fast(pool, size);
219 mp_alloc_noalign(struct mempool *pool, uns size)
221 return mp_alloc_fast_noalign(pool, size);
225 mp_alloc_zero(struct mempool *pool, uns size)
227 void *ptr = mp_alloc_fast(pool, size);
233 mp_start_internal(struct mempool *pool, uns size)
235 void *ptr = mp_alloc_internal(pool, size);
236 pool->state.free[pool->idx] += size;
241 mp_start(struct mempool *pool, uns size)
243 return mp_start_fast(pool, size);
247 mp_start_noalign(struct mempool *pool, uns size)
249 return mp_start_fast_noalign(pool, size);
253 mp_grow_internal(struct mempool *pool, uns size)
255 if (unlikely(size > MP_SIZE_MAX))
256 die("Cannot allocate %u bytes of memory", size);
257 uns avail = mp_avail(pool);
258 void *ptr = mp_ptr(pool);
261 uns amortized = likely(avail <= MP_SIZE_MAX / 2) ? avail * 2 : MP_SIZE_MAX;
262 amortized = MAX(amortized, size);
263 amortized = ALIGN_TO(amortized, CPU_STRUCT_ALIGN);
264 struct mempool_chunk *chunk = pool->state.last[1], *next = chunk->next;
265 ptr = xrealloc(ptr, amortized + MP_CHUNK_TAIL);
266 chunk = ptr + amortized;
268 chunk->size = amortized;
269 pool->state.last[1] = chunk;
270 pool->state.free[1] = amortized;
271 pool->last_big = ptr;
276 void *p = mp_start_internal(pool, size);
277 memcpy(p, ptr, avail);
283 mp_open(struct mempool *pool, void *ptr)
285 return mp_open_fast(pool, ptr);
289 mp_realloc(struct mempool *pool, void *ptr, uns size)
291 return mp_realloc_fast(pool, ptr, size);
295 mp_realloc_zero(struct mempool *pool, void *ptr, uns size)
297 uns old_size = mp_open_fast(pool, ptr);
298 ptr = mp_grow(pool, size);
300 bzero(ptr + old_size, size - old_size);
301 mp_end(pool, ptr + size);
306 mp_spread_internal(struct mempool *pool, void *p, uns size)
308 void *old = mp_ptr(pool);
309 void *new = mp_grow_internal(pool, p-old+size);
314 mp_restore(struct mempool *pool, struct mempool_state *state)
316 struct mempool_chunk *chunk, *next;
317 struct mempool_state s = *state;
318 for (chunk = pool->state.last[0]; chunk != s.last[0]; chunk = next)
321 chunk->next = pool->unused;
322 pool->unused = chunk;
324 for (chunk = pool->state.last[1]; chunk != s.last[1]; chunk = next)
327 mp_free_big_chunk(chunk);
330 pool->last_big = &pool->last_big;
333 struct mempool_state *
334 mp_push(struct mempool *pool)
336 struct mempool_state state = pool->state;
337 struct mempool_state *p = mp_alloc_fast(pool, sizeof(*p));
339 pool->state.next = p;
344 mp_pop(struct mempool *pool)
346 ASSERT(pool->state.next);
347 struct mempool_state state = pool->state;
348 mp_restore(pool, &state);
353 #include "ucw/getopt.h"
359 fill(byte *ptr, uns len, uns magic)
362 *ptr++ = (magic++ & 255);
366 check(byte *ptr, uns len, uns magic, uns align)
368 ASSERT(!((uintptr_t)ptr & (align - 1)));
370 if (*ptr++ != (magic++ & 255))
374 int main(int argc, char **argv)
379 if (cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) >= 0 || argc != optind)
380 die("Invalid usage");
382 uns max = 1000, n = 0, m = 0, can_realloc = 0;
384 struct mempool_state *state[max];
385 uns len[max], num[max], align[max];
386 struct mempool *mp = mp_new(128), mp_static;
388 for (uns i = 0; i < 5000; i++)
390 for (uns j = 0; j < n; j++)
391 check(ptr[j], len[j], j, align[j]);
393 DBG("free_small=%u free_big=%u idx=%u chunk_size=%u last_big=%p", mp->state.free[0], mp->state.free[1], mp->idx, mp->chunk_size, mp->last_big);
394 for (struct mempool_chunk *ch = mp->state.last[0]; ch; ch = ch->next)
395 DBG("small %p %p %p %d", (byte *)ch - ch->size, ch, ch + 1, ch->size);
396 for (struct mempool_chunk *ch = mp->state.last[1]; ch; ch = ch->next)
397 DBG("big %p %p %p %d", (byte *)ch - ch->size, ch, ch + 1, ch->size);
399 int r = random_max(100);
406 else if ((r -= 1) < 0)
411 mp = mp_new(random_max(0x1000) + 1);
413 mp = &mp_static, mp_init(mp, random_max(512) + 1);
416 else if (n < max && (r -= 30) < 0)
418 len[n] = random_max(0x2000);
419 DBG("alloc(%u)", len[n]);
420 align[n] = random_max(2) ? CPU_STRUCT_ALIGN : 1;
421 ptr[n] = (align[n] == 1) ? mp_alloc_fast_noalign(mp, len[n]) : mp_alloc_fast(mp, len[n]);
422 DBG(" -> (%p)", ptr[n]);
423 fill(ptr[n], len[n], n);
427 else if (n < max && (r -= 20) < 0)
429 len[n] = random_max(0x2000);
430 DBG("start(%u)", len[n]);
431 align[n] = random_max(2) ? CPU_STRUCT_ALIGN : 1;
432 ptr[n] = (align[n] == 1) ? mp_start_fast_noalign(mp, len[n]) : mp_start_fast(mp, len[n]);
433 DBG(" -> (%p)", ptr[n]);
434 fill(ptr[n], len[n], n);
439 else if (can_realloc && n && (r -= 10) < 0)
441 if (mp_open(mp, ptr[n - 1]) != len[n - 1])
446 for (uns i = random_max(4); i--; )
449 len[k] = random_max(0x2000);
450 DBG("grow(%u)", len[k]);
451 ptr[k] = mp_grow(mp, len[k]);
452 DBG(" -> (%p)", ptr[k]);
453 check(ptr[k], MIN(l, len[k]), k, align[k]);
454 fill(ptr[k], len[k], k);
456 mp_end(mp, ptr[k] + len[k]);
459 else if (can_realloc && n && (r -= 20) < 0)
461 uns i = n - 1, l = len[i];
462 DBG("realloc(%p, %u)", ptr[i], len[i]);
463 ptr[i] = mp_realloc(mp, ptr[i], len[i] = random_max(0x2000));
464 DBG(" -> (%p, %u)", ptr[i], len[i]);
465 check(ptr[i], MIN(len[i], l), i, align[i]);
466 fill(ptr[i], len[i], i);
468 else if (m < max && (r -= 5) < 0)
472 state[m++] = mp_push(mp);
475 else if (m && (r -= 2) < 0)
483 else if (m && (r -= 1) < 0)
485 uns i = random_max(m);
486 DBG("restore(%u)", i);
487 mp_restore(mp, state[i]);
491 else if (can_realloc && n && (r -= 5) < 0)
492 ASSERT(mp_size(mp, ptr[n - 1]) == len[n - 1]);