/* 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
SILC_ST_DEBUG(("Get stack block, si %d, size %lu", si, bsize));
+ silc_mutex_lock(stack->lock);
+
/* Get stack that has block that can house our size requirement. */
silc_list_start(stack->stacks);
while ((e = silc_list_get(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;
}
/* Allocate new stack blocks */
e = silc_calloc(1, sizeof(*e));
- if (!e)
+ if (!e) {
+ silc_mutex_unlock(stack->lock);
return NULL;
+ }
e->data[si] = silc_malloc(bsize + SILC_STACK_ALIGN(sizeof(*e->data[0]),
stack->alignment));
if (!e->data[si]) {
silc_free(e);
+ silc_mutex_unlock(stack->lock);
return NULL;
}
e->data[si]->bytes_left = bsize;
e->bsize = bsize;
SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
+
+ silc_mutex_unlock(stack->lock);
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 */
}
}
+ /* Allocate lock */
+ silc_mutex_alloc(&stack->lock);
+
/* Use the allocated stack in first stack frame */
stack->frame = &stack->frames[0];
stack->frame->prev = NULL;
SilcStackDataEntry e;
int i;
+ if (!stack)
+ return;
+
SILC_LOG_DEBUG(("Free stack %p", stack));
+ if (stack->lock)
+ silc_mutex_free(stack->lock);
+
if (!stack->parent) {
silc_list_start(stack->stacks);
while ((e = silc_list_get(stack->stacks))) {
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);
if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
SILC_LOG_ERROR(("Allocating too much"));
SILC_STACK_STAT(stack, num_errors, 1);
+ if (stack->oom_handler)
+ stack->oom_handler(stack, stack->oom_context);
return NULL;
}
if (silc_unlikely(si >= SILC_STACK_BLOCK_NUM)) {
SILC_LOG_ERROR(("Allocating too large block"));
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;
if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
SILC_LOG_ERROR(("Allocating too much"));
SILC_STACK_STAT(stack, num_errors, 1);
+ if (stack->oom_handler)
+ stack->oom_handler(stack, stack->oom_context);
return NULL;
}
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)
* silc_buffer_sstrformat, silc_buffer_senlarge, silc_mp_sinit,
* silc_dlist_sinit, silc_hash_table_alloc
*
- * The SilcStack context is not thread-safe. If the same SilcStack must be
- * used in multithreaded environment concurrency control must be employed.
- * Each thread should allocate their own SilcStack.
+ * The SilcStack is not thread-safe so that same context could be used for
+ * allocations from multiple threads. It is however safe to create and use
+ * child stacks in a different thread from the parent stack. Each thread
+ * should allocate their own SilcStack, however they may be child stacks.
*
***/
***/
typedef struct SilcStackFrameStruct SilcStackFrame;
+/****f* silcutil/SilcStackAPI/SilcStackOomHandler
+ *
+ * SYNOPSIS
+ *
+ * typedef void (*SilcStackOomHandler)(SilcStack stack, void *context);
+ *
+ * DESCRIPTION
+ *
+ * Callback of this type can be given to silc_stack_set_oom_handler
+ * to set Out of Memory handler to `stack'. If memory allocation from
+ * `stack' fails this callback is called to indicate error. The `context'
+ * is the context given to silc_stack_set_oom_handler.
+ *
+ ***/
+typedef void (*SilcStackOomHandler)(SilcStack stack, void *context);
+
/****f* silcutil/SilcStackAPI/silc_stack_alloc
*
* SYNOPSIS
* allocation by various routines. Returns the pointer to the stack
* that must be freed with silc_stack_free function when it is not
* needed anymore. If the `stack_size' is zero (0) by default a
- * 2 kilobytes (2048 bytes) stack is allocated. If the `stack_size'
+ * 1 kilobyte (1024 bytes) stack is allocated. If the `stack_size'
* is non-zero the byte value must be multiple by 8.
*
* If `parent' is non-NULL the created stack is a child of the `parent'
* stack. All of childs the memory is allocated from the `parent' and
* will be returned back to the parent when the child is freed. Note
- * that, even though child allocated memory from the parent, the parent's
+ * that, even though child allocates memory from the parent, the parent's
* stack is not consumed.
*
* Returns NULL on error.
* this and all allocated memory are freed.
*
* If `stack' is a child stack, its memory is returned back to its
- * parent.
+ * parent. If `stack' is NULL this function does nothing.
*
***/
void silc_stack_free(SilcStack stack);
* stack and all allocated memory is freed after the next silc_stack_pop
* is called. This returns so called stack pointer for the new stack
* frame, which the caller may use to check that all calls to
- * silc_stack_pop has been made. This call may do a small memory
- * allocation in some cases, but usually it does not allocate any memory.
- * If this returns zero (0) the system is out of memory.
+ * silc_stack_pop has been made.
*
* If the `frame' is non-NULL then that SilcStackFrame is used as
* stack frame. Usually `frame' is set to NULL by user. Statically
* allocated SilcStackFrame should be used when using silc_stack_push
* in recursive function and the recursion may become deep. In this
* case using statically allocated SilcStackFrame is recommended since
- * it assures that frames never run out and silc_stack_push never
- * allocates any memory. If your routine is not recursive then
- * setting `frame' to NULL is recommended, unless performance is
- * critical.
+ * it assures that frames never run out. If your routine is not recursive
+ * then setting `frame' to NULL is recommended.
*
* This function is used when a routine is doing frequent allocations
* from the stack. If the stack is not pushed and later popped all
* (it gets enlarged by normal memory allocation). By pushing and then
* later popping the frequent allocations does not consume the stack.
*
- * If `stack' is NULL this call has no effect.
+ * If `stack' is NULL this call has no effect. This function does not
+ * allocate any memory.
*
* EXAMPLE
*
*
* DESCRIPTION
*
- * Pop the top of the stack upwards which reveals the previous stack frame
- * and becomes the top of the stack. After popping, memory allocated in
+ * Pop the top of the stack which removes the previous stack frame and
+ * becomes the top of the stack. After popping, memory allocated in
* the old frame is freed. For each silc_stack_push call there must be
* silc_stack_pop call to free all memory (in reality any memory is not
* freed but within the stack it is). This returns the stack pointer of
* silc_stack_pop has been called too many times. Application should
* treat this as a fatal error, as it is a bug in the application code.
*
- * If `stack' is NULL this call has no effect.
+ * If `stack' is NULL this call has no effect. This function does not
+ * allocate any memory.
*
* EXAMPLE
*
*
* DESCRIPTION
*
- * Low level memory allocation routine. Allocates memor block of size of
+ * Low level memory allocation routine. Allocates memory block of size of
* `size' from the `stack'. The allocated memory is aligned so it can be
* used to allocate memory for structures, for example. Returns the
* allocated memory address or NULL if memory could not be allocated from
* NOTES
*
* This function should be used only if low level memory allocation with
- * SilcStack is needed. Instead, silc_smalloc, could be used.
+ * SilcStack is needed. Instead, silc_smalloc and silc_scalloc could
+ * be used.
*
***/
void *silc_stack_malloc(SilcStack stack, SilcUInt32 size);
* `size'. This routine works only if the previous allocation to `stack'
* was `ptr'. If there is another memory allocation between allocating
* `ptr' and this call this routine will return NULL. NULL is also
- * returned if the `size' does not fit into the current block. If NULL
- * is returned the old memory remains intact.
+ * returned if the `size' does not fit into the current stack block.
+ * If NULL is returned the old memory remains intact.
*
* NOTES
*
* This function should be used only if low level memory allocation with
- * SilcStack is needed. Instead, silc_srealloc, could be used.
+ * SilcStack is needed. Instead, silc_srealloc could be used.
*
***/
void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
void *ptr, SilcUInt32 size);
+/****f* silcutil/SilcStackAPI/silc_stack_set_oom_handler
+ *
+ * SYNOPSIS
+ *
+ * void silc_stack_set_oom_handler(SilcStack stack,
+ * SilcStackOomHandler oom_handler,
+ * void *context);
+ *
+ * DESCRIPTION
+ *
+ * Sets Out of Memory handler `oom_handler' to `stack' to be called
+ * if memory allocation from `stack' fails. The `context' is delivered
+ * to `oom_handler'.
+ *
+ * Usually Out of Memory handler is set only when failed memory allocation
+ * is a fatal error. In this case the application would abort() inside
+ * the `oom_handler'. It may also be set if in case of failed allocation
+ * application wants to do clean up properly.
+ *
+ ***/
+void silc_stack_set_oom_handler(SilcStack stack,
+ SilcStackOomHandler oom_handler,
+ void *context);
+
/****f* silcutil/SilcStackAPI/silc_stack_set_alignment
*
* SYNOPSIS
* Sets/changes the memory alignment in the `stack' to `alignment' which
* is the alignment in bytes. By default, the SilcStack will use alignment
* suited for the platform where it is used. This function can be used
- * change this alignment, if such change is needed. You may check the
+ * to change this alignment, if such change is needed. You may check the
* current alignment by calling silc_stack_get_alignment.
*
* NOTES