5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2003 - 2008 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 #include "silcruntime.h"
22 /************************** Types and definitions ***************************/
24 /* The SilcStack context */
25 struct SilcStackStruct {
26 SilcStack parent; /* Parent stack */
27 SilcMutex lock; /* Stack lock */
28 SilcList stacks; /* List of stacks for childs */
29 SilcStackDataEntry stack; /* The allocated stack */
30 SilcStackFrame *frames; /* Allocated stack frames */
31 SilcStackFrame *frame; /* Current stack frame */
32 SilcStackOomHandler oom_handler; /* OOM handler */
33 void *oom_context; /* OOM handler context */
34 SilcUInt32 stack_size; /* Default stack size */
35 SilcUInt32 alignment; /* Memory alignment */
36 SilcAtomic32 refcnt; /* Reference counter */
37 #ifdef SILC_DIST_INPLACE
39 SilcUInt32 snum_malloc;
40 SilcUInt32 sbytes_malloc;
41 SilcUInt32 snum_errors;
42 #endif /* SILC_DIST_INPLACE */
45 /************************ Static utility functions **************************/
47 /* Compute stack block index for the `size'. */
49 static SilcUInt32 silc_stack_get_index(SilcUInt32 size, SilcUInt32 *ret_bsize)
53 if (size < SILC_STACK_DEFAULT_SIZE)
54 size = SILC_STACK_DEFAULT_SIZE;
56 bsize = SILC_STACK_DEFAULT_SIZE;
57 while (bsize < size) {
67 /* Get stack from `stack' or allocate new one. */
69 static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack,
72 SilcUInt32 *ret_bsize)
77 /* Get stack block index and block size for requested size */
78 si = silc_stack_get_index(size, &bsize);
82 SILC_ST_DEBUG(("Get stack block, si %d, size %lu, stack %p",
85 silc_mutex_lock(stack->lock);
87 /* Get stack that has block that can house our size requirement. */
88 silc_list_start(stack->stacks);
89 while ((e = silc_list_get(stack->stacks))) {
93 silc_list_del(stack->stacks, e);
94 SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
95 silc_mutex_unlock(stack->lock);
99 silc_mutex_unlock(stack->lock);
101 /* If we are child, get block from parent */
103 return silc_stack_ref_stack(stack->parent, size, ret_si, ret_bsize);
105 SILC_ST_DEBUG(("Allocate new stack blocks"));
107 /* Allocate new stack blocks */
108 e = silc_calloc(1, sizeof(*e));
111 e->data[si] = silc_malloc(bsize + SILC_STACK_ALIGN(sizeof(*e->data[0]),
117 e->data[si]->bytes_left = bsize;
121 SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
126 /* Return the `data' back to the `stack'. */
128 static void silc_stack_unref_stack(SilcStack stack, SilcStackDataEntry e)
132 SILC_LOG_DEBUG(("Release stack blocks %p to stack %p, si %d",
133 e->data, stack, e->si));
135 /* Release all blocks from allocations */
136 for (i = e->si; i < SILC_STACK_BLOCK_NUM; i++) {
140 e->data[i]->bytes_left = e->bsize;
142 e->data[i]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, i);
145 silc_mutex_lock(stack->lock);
146 silc_list_add(stack->stacks, e);
147 silc_mutex_unlock(stack->lock);
150 /* Allocate memory from a specific stack block */
152 static void *silc_stack_alloc_block(SilcStack stack, SilcStackDataEntry e,
153 SilcUInt32 items, SilcUInt32 size)
158 /* Get pointer and consume the stack block */
159 asize = SILC_STACK_ALIGN(items * size, stack->alignment);
160 ptr = SILC_STACK_DATA_EXT(e->data, e->si, e->bsize, stack->alignment);
161 e->data[e->si]->bytes_left -= asize;
162 memset(ptr, 0, items * size);
167 /***************************** SilcStack API ********************************/
169 /* Allocate the stack */
171 SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
174 SilcStackDataEntry e;
175 SilcUInt32 si = 0, bsize = 0;
177 stack_size = stack_size ? stack_size : SILC_STACK_DEFAULT_SIZE;
178 if (stack_size < SILC_STACK_DEFAULT_SIZE)
179 stack_size = SILC_STACK_DEFAULT_SIZE;
182 stack_size += ((-stack_size) % 8);
185 /* Get stack from parent. The stack itself is allocated from the
186 parent (but does not consume parent's own stack). */
187 e = silc_stack_ref_stack(parent, stack_size, &si, &bsize);
191 /* Allocate stack from the returned stack. We allocate ourselves from
193 stack = silc_stack_alloc_block(parent, e, 1, sizeof(*stack));
195 silc_stack_unref_stack(parent, e);
199 stack->parent = parent;
200 stack->stack_size = stack_size;
201 stack->alignment = SILC_STACK_DEFAULT_ALIGN;
202 stack->oom_handler = parent->oom_handler;
203 stack->oom_context = parent->oom_context;
204 stack->lock = parent->lock;
205 silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
207 /* Allocate stack frames from the stack itself */
208 stack->frames = silc_stack_alloc_block(stack, e, SILC_STACK_BLOCK_NUM,
209 sizeof(*stack->frames));
210 if (!stack->frames) {
211 silc_stack_unref_stack(parent, e);
215 /* Reference parent */
216 SILC_LOG_DEBUG(("Reference stack %p, refcnt %d > %d", stack->parent,
217 silc_atomic_get_int32(&stack->parent->refcnt),
218 silc_atomic_get_int32(&stack->parent->refcnt) + 1));
219 silc_atomic_add_int32(&stack->parent->refcnt, 1);
221 /* Set the initial stack */
224 /* Dynamically allocate new stack */
225 stack = silc_calloc(1, sizeof(*stack));
229 stack->stack_size = stack_size;
230 stack->alignment = SILC_STACK_DEFAULT_ALIGN;
231 silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
233 /* Create initial stack */
234 stack->stack = silc_calloc(1, sizeof(*stack->stack));
239 stack->stack->data[0] =
240 silc_malloc(stack->stack_size +
241 SILC_STACK_ALIGN(sizeof(*stack->stack->data[0]),
243 if (!stack->stack->data[0]) {
244 silc_free(stack->stack);
248 stack->stack->data[0]->bytes_left = stack->stack_size;
249 stack->stack->si = 0;
250 stack->stack->bsize = stack->stack_size;
252 /* Allocate stack frames from the stack itself */
253 stack->frames = silc_stack_alloc_block(stack, stack->stack,
254 SILC_STACK_DEFAULT_NUM,
255 sizeof(*stack->frames));
256 if (!stack->frames) {
257 silc_free(stack->stack->data[0]);
258 silc_free(stack->stack);
264 silc_mutex_alloc(&stack->lock);
267 silc_atomic_init32(&stack->refcnt, 1);
269 /* Use the allocated stack in first stack frame */
270 stack->frame = &stack->frames[0];
271 stack->frame->prev = NULL;
272 stack->frame->bytes_used = stack->stack_size;
273 stack->frame->sp = 1;
274 stack->frame->si = si;
276 SILC_LOG_DEBUG(("New stack %p, size %d bytes", stack, stack->stack_size));
281 /* Frees the stack and all allocated memory */
283 void silc_stack_free(SilcStack stack)
285 SilcStackDataEntry e;
291 SILC_LOG_DEBUG(("Free stack %p, refcnt %d > %d", stack,
292 silc_atomic_get_int32(&stack->refcnt),
293 silc_atomic_get_int32(&stack->refcnt) - 1));
296 if (silc_atomic_sub_int32(&stack->refcnt, 1) > 0)
299 if (!stack->parent) {
300 silc_list_start(stack->stacks);
301 while ((e = silc_list_get(stack->stacks))) {
302 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
303 silc_free(e->data[i]);
307 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
308 silc_free(stack->stack->data[i]);
309 silc_free(stack->stack);
312 silc_mutex_free(stack->lock);
314 silc_atomic_uninit32(&stack->refcnt);
318 /* Return all stack blocks to the parent */
319 silc_list_start(stack->stacks);
320 while ((e = silc_list_get(stack->stacks)))
321 silc_stack_unref_stack(stack->parent, e);
323 silc_stack_unref_stack(stack->parent, stack->stack);
325 /* Unreference parent */
326 silc_stack_free(stack->parent);
330 /* Push to next stack frame */
332 SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame)
338 if (stack->frame->sp >= SILC_STACK_ALIGN(stack->frame->sp,
339 SILC_STACK_DEFAULT_NUM)) {
340 SILC_LOG_DEBUG(("SilcStack %p running out of frames, cannot push",
342 return stack->frame->sp;
345 frame = &stack->frames[stack->frame->sp];
349 frame->prev = stack->frame;
350 frame->sp = stack->frame->sp + 1;
351 frame->si = stack->frame->si;
352 frame->bytes_used = stack->stack->data[frame->si]->bytes_left;
353 stack->frame = frame;
355 SILC_ST_DEBUG(("Push %p: sp %d -> %d, si %d", stack, frame->prev->sp,
356 frame->sp, frame->si));
358 return stack->frame->sp;
361 /* Pop to previous stack frame */
363 SilcUInt32 silc_stack_pop(SilcStack stack)
367 if (!stack || !stack->frame->prev)
371 si = stack->frame->si;
372 while (si > stack->frame->prev->si) {
373 if (stack->stack->data[si])
374 stack->stack->data[si]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, si);
377 stack->stack->data[si]->bytes_left = stack->frame->bytes_used;
378 stack->frame = stack->frame->prev;
380 SILC_ST_DEBUG(("Pop %p: sp %d -> %d, si %d", stack, stack->frame->sp + 1,
381 stack->frame->sp, stack->frame->si));
383 return stack->frame->sp + 1;
386 /* Allocate memory. Returns pointer to the memory or NULL on error. */
388 void *silc_stack_malloc(SilcStack stack, SilcUInt32 size)
391 SilcUInt32 bsize, bsize2;
392 SilcUInt32 si = stack->frame->si;
394 SILC_STACK_STAT(stack, num_malloc, 1);
395 SILC_ST_DEBUG(("Allocating %d bytes from %p", size, stack));
397 if (silc_unlikely(!size)) {
398 SILC_LOG_DEBUG(("Allocation by zero (0)"));
399 silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
400 SILC_STACK_STAT(stack, num_errors, 1);
404 if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
405 SILC_LOG_DEBUG(("Allocating too much"));
406 silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
407 SILC_STACK_STAT(stack, num_errors, 1);
408 if (stack->oom_handler)
409 stack->oom_handler(stack, stack->oom_context);
414 size = SILC_STACK_ALIGN(size, stack->alignment);
416 /* Compute the size of current stack block */
417 bsize = SILC_STACK_BLOCK_SIZE(stack, si);
419 /* See if there is space in the current stack block */
420 if (stack->stack->data[si]->bytes_left >= size) {
421 /* Get pointer to the memory */
422 ptr = SILC_STACK_DATA(stack, si, bsize);
423 stack->stack->data[si]->bytes_left -= size;
424 SILC_STACK_STAT(stack, bytes_malloc, size);
428 /* There is not enough space in this block. Find the spot to stack
429 block that can handle this size memory. */
430 if (bsize < SILC_STACK_DEFAULT_SIZE)
431 bsize = SILC_STACK_DEFAULT_SIZE;
433 bsize2 = SILC_STACK_DEFAULT_SIZE;
435 while (bsize2 < bsize) {
439 if (silc_unlikely(si >= SILC_STACK_BLOCK_NUM)) {
440 SILC_LOG_DEBUG(("Allocating too large block"));
441 silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
442 SILC_STACK_STAT(stack, num_errors, 1);
443 if (stack->oom_handler)
444 stack->oom_handler(stack, stack->oom_context);
448 /* Allocate the block if it doesn't exist yet */
449 if (!stack->stack->data[si]) {
450 SILC_ST_DEBUG(("Allocating new stack block, %d bytes", bsize2));
451 stack->stack->data[si] =
453 SILC_STACK_ALIGN(sizeof(**stack->stack->data),
455 if (silc_unlikely(!stack->stack->data[si])) {
456 SILC_STACK_STAT(stack, num_errors, 1);
457 if (stack->oom_handler)
458 stack->oom_handler(stack, stack->oom_context);
461 stack->stack->data[si]->bytes_left = bsize2;
464 /* Now return memory from this new block. It is guaranteed that in this
465 block there is enough space for this memory. */
466 assert(stack->stack->data[si]->bytes_left >= size);
467 ptr = SILC_STACK_DATA(stack, si, bsize2);
468 stack->stack->data[si]->bytes_left -= size;
469 stack->frame->si = si;
470 SILC_STACK_STAT(stack, bytes_malloc, size);
475 /* Attempts to reallocate memory by changing the size of the `ptr' into
476 `size'. This routine works only if the previous allocation to `stack'
477 was `ptr'. If there is another memory allocation between allocating
478 `ptr' and this call this routine will return NULL. NULL is also returned
479 if the `size' does not fit into the current block. If NULL is returned
480 the old memory remains intact. */
482 void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
483 void *ptr, SilcUInt32 size)
485 SilcUInt32 si = stack->frame->si;
490 return silc_stack_malloc(stack, size);
492 SILC_STACK_STAT(stack, num_malloc, 1);
493 SILC_ST_DEBUG(("Reallocating %d bytes (%d) from %p", size, old_size, stack));
495 if (silc_unlikely(!size || !old_size)) {
496 SILC_LOG_DEBUG(("Allocation by zero (0)"));
497 silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
498 SILC_STACK_STAT(stack, num_errors, 1);
502 if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
503 SILC_LOG_DEBUG(("Allocating too much"));
504 silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
505 SILC_STACK_STAT(stack, num_errors, 1);
506 if (stack->oom_handler)
507 stack->oom_handler(stack, stack->oom_context);
512 old_size = SILC_STACK_ALIGN(old_size, stack->alignment);
514 /* Compute the size of current stack block */
515 bsize = SILC_STACK_BLOCK_SIZE(stack, si);
517 /* Check that `ptr' is last allocation */
518 sptr = (unsigned char *)stack->stack->data[si] +
519 SILC_STACK_ALIGN(sizeof(**stack->stack->data), stack->alignment);
520 if (stack->stack->data[si]->bytes_left + old_size +
521 ((unsigned char *)ptr - (unsigned char *)sptr) != bsize) {
522 SILC_LOG_DEBUG(("Cannot reallocate"));
523 silc_set_errno_nofail(SILC_ERR_INVALID_ARGUMENT);
524 SILC_STACK_STAT(stack, num_errors, 1);
528 /* Now check that the new size fits to this block */
529 if (stack->stack->data[si]->bytes_left >= size) {
530 /* It fits, so simply return the old pointer */
531 size = SILC_STACK_ALIGN(size, stack->alignment);
532 stack->stack->data[si]->bytes_left -= (size - old_size);
533 SILC_STACK_STAT(stack, bytes_malloc, (size - old_size));
537 SILC_LOG_DEBUG(("Cannot reallocate in this block"));
538 silc_set_errno_reason_nofail(SILC_ERR_TOO_LARGE_ALLOCATION,
539 "Cannot reallocate in this memory block");
540 SILC_STACK_STAT(stack, num_errors, 1);
544 /* Set OOM handler */
546 void silc_stack_set_oom_handler(SilcStack stack,
547 SilcStackOomHandler oom_handler,
550 stack->oom_handler = oom_handler;
551 stack->oom_context = context;
554 /* Set default alignment */
556 void silc_stack_set_alignment(SilcStack stack, SilcUInt32 alignment)
558 SILC_LOG_DEBUG(("Set stack %p alignment to %d bytes", stack, alignment));
559 stack->alignment = alignment;
562 /* Get default alignment */
564 SilcUInt32 silc_stack_get_alignment(SilcStack stack)
566 return stack->alignment;
571 SilcBool silc_stack_purge(SilcStack stack)
573 SilcStackDataEntry e;
574 SilcBool ret = FALSE;
577 SILC_LOG_DEBUG(("Purge stack %p", stack));
579 /* Go through the default stack */
580 for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
581 if (stack->stack->data[i] &&
582 stack->stack->data[i]->bytes_left == SILC_STACK_BLOCK_SIZE(stack, i)) {
583 SILC_LOG_DEBUG(("Purge %d bytes",
584 SILC_STACK_BLOCK_SIZE(stack, i)));
585 silc_free(stack->stack->data[i]);
586 stack->stack->data[i] = NULL;
591 silc_mutex_lock(stack->lock);
593 /* Remove one child stack */
594 if (silc_list_count(stack->stacks) > 2) {
595 silc_list_start(stack->stacks);
596 e = silc_list_get(stack->stacks);
598 SILC_LOG_DEBUG(("Remove stack blocks %p", e->data));
599 silc_list_del(stack->stacks, e);
602 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
603 silc_free(e->data[i]);
607 /* Go through the child stacks */
608 silc_list_start(stack->stacks);
609 while ((e = silc_list_get(stack->stacks))) {
610 for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
612 SILC_LOG_DEBUG(("Purge %d bytes",
613 SILC_STACK_BLOCK_SIZE(stack, i)));
614 silc_free(e->data[i]);
621 silc_mutex_unlock(stack->lock);
626 /* Set global stack */
628 void silc_stack_set_global(SilcStack stack)
630 SilcTls tls = silc_thread_get_tls();
633 /* Try to initialize Tls */
634 tls = silc_thread_tls_init();
643 /* Return global stack */
645 SilcStack silc_stack_get_global(void)
647 SilcTls tls = silc_thread_get_tls();
655 #ifdef SILC_DIST_INPLACE
656 /* Statistics dumping. */
658 void silc_stack_stats(SilcStack stack)
660 SilcStackDataEntry e;
661 SilcUInt32 stack_size = 0;
664 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
665 if (!stack->stack->data[i])
667 stack_size += SILC_STACK_BLOCK_SIZE(stack, i);
671 fprintf(stdout, "\nSilcStack %p statistics :\n\n", stack);
672 fprintf(stdout, " Size of stack : %u\n",
673 (unsigned int)stack_size);
674 fprintf(stdout, " Stack alignment : %d\n",
675 (int)stack->alignment);
676 fprintf(stdout, " Number of allocs : %u\n",
677 (unsigned int)stack->snum_malloc);
678 fprintf(stdout, " Bytes allocated : %u\n",
679 (unsigned int)stack->sbytes_malloc);
680 fprintf(stdout, " Average alloc size : %.2f\n",
681 (double)((double)stack->sbytes_malloc / (double)stack->snum_malloc));
682 fprintf(stdout, " Number of alloc errors : %u\n",
683 (unsigned int)stack->snum_errors);
684 fprintf(stdout, " Number of frames : %u\n",
685 (unsigned int)SILC_STACK_ALIGN(stack->frame->sp,
686 SILC_STACK_DEFAULT_NUM));
687 fprintf(stdout, " Number of blocks : %u\n", c);
688 fprintf(stdout, " Number of stacks : %d\n",
689 silc_list_count(stack->stacks));
691 silc_list_start(stack->stacks);
692 while ((e = silc_list_get(stack->stacks))) {
695 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
698 stack_size += e->data[i]->bytes_left;
701 fprintf(stdout, "\n Size of stack : %u\n",
702 (unsigned int)stack_size);
703 fprintf(stdout, " Number of blocks : %u\n", c);
706 #endif /* SILC_DIST_INPLACE */