Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2003 - 2007 Pekka Riikonen
+ Copyright (C) 2003 - 2008 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/* The SilcStack context */
struct SilcStackStruct {
SilcStack parent; /* Parent stack */
- SilcList stacks; /* List of stacks */
- SilcStackDataEntry stack; /* Allocated stack blocks */
+ SilcMutex lock; /* Stack lock */
+ SilcList stacks; /* List of stacks for childs */
+ SilcStackDataEntry stack; /* The allocated stack */
SilcStackFrame *frames; /* Allocated stack frames */
SilcStackFrame *frame; /* Current stack frame */
+ SilcStackOomHandler oom_handler; /* OOM handler */
+ void *oom_context; /* OOM handler context */
SilcUInt32 stack_size; /* Default stack size */
SilcUInt32 alignment; /* Memory alignment */
#ifdef SILC_DIST_INPLACE
*ret_si = si;
*ret_bsize = bsize;
- SILC_ST_DEBUG(("Get stack block, si %d, size %lu", si, bsize));
+ SILC_ST_DEBUG(("Get stack block, si %d, size %lu, stack %p",
+ si, bsize, stack));
+
+ silc_mutex_lock(stack->lock);
/* Get stack that has block that can house our size requirement. */
silc_list_start(stack->stacks);
silc_list_del(stack->stacks, e);
SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
+ silc_mutex_unlock(stack->lock);
return e;
}
+ silc_mutex_unlock(stack->lock);
+
+ /* If we are child, get block from parent */
+ if (stack->parent)
+ return silc_stack_ref_stack(stack->parent, size, ret_si, ret_bsize);
+
SILC_ST_DEBUG(("Allocate new stack blocks"));
/* Allocate new stack blocks */
e->bsize = bsize;
SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
+
return e;
}
e->data[i]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, i);
}
+ silc_mutex_lock(stack->lock);
silc_list_add(stack->stacks, e);
+ silc_mutex_unlock(stack->lock);
}
/* Allocate memory from a specific stack block */
if (stack_size < SILC_STACK_DEFAULT_SIZE)
stack_size = SILC_STACK_DEFAULT_SIZE;
+ /* Align by 8 */
+ stack_size += ((-stack_size) % 8);
+
if (parent) {
/* Get stack from parent. The stack itself is allocated from the
- parent. */
+ parent (but does not consume parent's own stack). */
e = silc_stack_ref_stack(parent, stack_size, &si, &bsize);
if (!e)
return NULL;
- /* Allocate stack from the parent */
+ /* Allocate stack from the returned stack. We allocate ourselves from
+ our own stack. */
stack = silc_stack_alloc_block(parent, e, 1, sizeof(*stack));
if (!stack) {
silc_stack_unref_stack(parent, e);
stack->parent = parent;
stack->stack_size = stack_size;
stack->alignment = SILC_STACK_DEFAULT_ALIGN;
+ stack->oom_handler = parent->oom_handler;
+ stack->oom_context = parent->oom_context;
+ stack->lock = parent->lock;
silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
- /* Allocate stack frames from the parent */
- stack->frames = silc_stack_alloc_block(parent, e, SILC_STACK_BLOCK_NUM,
+ /* Allocate stack frames from the stack itself */
+ stack->frames = silc_stack_alloc_block(stack, e, SILC_STACK_BLOCK_NUM,
sizeof(*stack->frames));
if (!stack->frames) {
silc_stack_unref_stack(parent, e);
silc_free(stack);
return NULL;
}
+
+ /* Allocate lock */
+ silc_mutex_alloc(&stack->lock);
}
/* Use the allocated stack in first stack frame */
SilcStackDataEntry e;
int i;
+ if (!stack)
+ return;
+
SILC_LOG_DEBUG(("Free stack %p", stack));
if (!stack->parent) {
silc_free(stack->stack->data[i]);
silc_free(stack->stack);
+ if (stack->lock)
+ silc_mutex_free(stack->lock);
+
silc_free(stack);
} else {
+ /* Return all stack blocks to the parent */
silc_list_start(stack->stacks);
while ((e = silc_list_get(stack->stacks)))
silc_stack_unref_stack(stack->parent, e);
SILC_ST_DEBUG(("Allocating %d bytes from %p", size, stack));
if (silc_unlikely(!size)) {
- SILC_LOG_ERROR(("Allocation by zero (0)"));
+ SILC_LOG_DEBUG(("Allocation by zero (0)"));
+ silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
SILC_STACK_STAT(stack, num_errors, 1);
return NULL;
}
if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
- SILC_LOG_ERROR(("Allocating too much"));
+ SILC_LOG_DEBUG(("Allocating too much"));
+ silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
SILC_STACK_STAT(stack, num_errors, 1);
+ if (stack->oom_handler)
+ stack->oom_handler(stack, stack->oom_context);
return NULL;
}
si++;
}
if (silc_unlikely(si >= SILC_STACK_BLOCK_NUM)) {
- SILC_LOG_ERROR(("Allocating too large block"));
+ SILC_LOG_DEBUG(("Allocating too large block"));
+ silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
SILC_STACK_STAT(stack, num_errors, 1);
+ if (stack->oom_handler)
+ stack->oom_handler(stack, stack->oom_context);
return NULL;
}
stack->alignment));
if (silc_unlikely(!stack->stack->data[si])) {
SILC_STACK_STAT(stack, num_errors, 1);
+ if (stack->oom_handler)
+ stack->oom_handler(stack, stack->oom_context);
return NULL;
}
stack->stack->data[si]->bytes_left = bsize2;
SILC_ST_DEBUG(("Reallocating %d bytes (%d) from %p", size, old_size, stack));
if (silc_unlikely(!size || !old_size)) {
- SILC_LOG_ERROR(("Allocation by zero (0)"));
+ SILC_LOG_DEBUG(("Allocation by zero (0)"));
+ silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
SILC_STACK_STAT(stack, num_errors, 1);
return NULL;
}
if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
- SILC_LOG_ERROR(("Allocating too much"));
+ SILC_LOG_DEBUG(("Allocating too much"));
+ silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
SILC_STACK_STAT(stack, num_errors, 1);
+ if (stack->oom_handler)
+ stack->oom_handler(stack, stack->oom_context);
return NULL;
}
if (stack->stack->data[si]->bytes_left + old_size +
((unsigned char *)ptr - (unsigned char *)sptr) != bsize) {
SILC_LOG_DEBUG(("Cannot reallocate"));
+ silc_set_errno_nofail(SILC_ERR_INVALID_ARGUMENT);
SILC_STACK_STAT(stack, num_errors, 1);
return NULL;
}
}
SILC_LOG_DEBUG(("Cannot reallocate in this block"));
+ silc_set_errno_reason_nofail(SILC_ERR_TOO_LARGE_ALLOCATION,
+ "Cannot reallocate in this memory block");
SILC_STACK_STAT(stack, num_errors, 1);
return NULL;
}
+/* Set OOM handler */
+
+void silc_stack_set_oom_handler(SilcStack stack,
+ SilcStackOomHandler oom_handler,
+ void *context)
+{
+ stack->oom_handler = oom_handler;
+ stack->oom_context = context;
+}
+
/* Set default alignment */
void silc_stack_set_alignment(SilcStack stack, SilcUInt32 alignment)
return stack->alignment;
}
+/* Purge stack */
+
+SilcBool silc_stack_purge(SilcStack stack)
+{
+ SilcStackDataEntry e;
+ SilcBool ret = FALSE;
+ int i;
+
+ SILC_LOG_DEBUG(("Purge stack %p", stack));
+
+ /* Go through the default stack */
+ for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
+ if (stack->stack->data[i] &&
+ stack->stack->data[i]->bytes_left == SILC_STACK_BLOCK_SIZE(stack, i)) {
+ SILC_LOG_DEBUG(("Purge %d bytes",
+ SILC_STACK_BLOCK_SIZE(stack, i)));
+ silc_free(stack->stack->data[i]);
+ stack->stack->data[i] = NULL;
+ ret = TRUE;
+ }
+ }
+
+ silc_mutex_lock(stack->lock);
+
+ /* Remove one child stack */
+ if (silc_list_count(stack->stacks) > 2) {
+ silc_list_start(stack->stacks);
+ e = silc_list_get(stack->stacks);
+
+ SILC_LOG_DEBUG(("Remove stack blocks %p", e->data));
+ silc_list_del(stack->stacks, e);
+ ret = TRUE;
+
+ for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
+ silc_free(e->data[i]);
+ silc_free(e);
+ }
+
+ /* Go through the child stacks */
+ silc_list_start(stack->stacks);
+ while ((e = silc_list_get(stack->stacks))) {
+ for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
+ if (e->data[i]) {
+ SILC_LOG_DEBUG(("Purge %d bytes",
+ SILC_STACK_BLOCK_SIZE(stack, i)));
+ silc_free(e->data[i]);
+ e->data[i] = NULL;
+ ret = TRUE;
+ }
+ }
+ }
+
+ silc_mutex_unlock(stack->lock);
+
+ return ret;
+}
+
+/* Set global stack */
+
+void silc_stack_set_global(SilcStack stack)
+{
+ SilcTls tls = silc_thread_get_tls();
+
+ if (!tls) {
+ /* Try to initialize Tls */
+ tls = silc_thread_tls_init();
+ SILC_VERIFY(tls);
+ if (!tls)
+ return;
+ }
+
+ tls->stack = stack;
+}
+
+/* Return global stack */
+
+SilcStack silc_stack_get_global(void)
+{
+ SilcTls tls = silc_thread_get_tls();
+
+ if (!tls)
+ return NULL;
+
+ return tls->stack;
+}
+
#ifdef SILC_DIST_INPLACE
/* Statistics dumping. */