From 43e2881abed8abfd54a27332e2def0178ddccea5 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Wed, 4 Jul 2007 13:29:37 +0000 Subject: [PATCH] Added multi-thread support. Added OOM handler. --- lib/silcutil/silcstack.c | 47 +++++++++++++++++++-- lib/silcutil/silcstack.h | 86 ++++++++++++++++++++++++++++---------- lib/silcutil/silcstack_i.h | 4 +- 3 files changed, 109 insertions(+), 28 deletions(-) diff --git a/lib/silcutil/silcstack.c b/lib/silcutil/silcstack.c index 7f179eb4..e314d8d0 100644 --- a/lib/silcutil/silcstack.c +++ b/lib/silcutil/silcstack.c @@ -24,10 +24,13 @@ /* 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 @@ -77,6 +80,8 @@ static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack, 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))) { @@ -85,6 +90,7 @@ static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack, 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; } @@ -92,12 +98,15 @@ static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack, /* 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; @@ -105,6 +114,8 @@ static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack, e->bsize = bsize; SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack)); + + silc_mutex_unlock(stack->lock); return e; } @@ -127,7 +138,9 @@ static void silc_stack_unref_stack(SilcStack stack, SilcStackDataEntry 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 */ @@ -231,6 +244,9 @@ SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent) } } + /* Allocate lock */ + silc_mutex_alloc(&stack->lock); + /* Use the allocated stack in first stack frame */ stack->frame = &stack->frames[0]; stack->frame->prev = NULL; @@ -250,8 +266,14 @@ void silc_stack_free(SilcStack stack) 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))) { @@ -266,6 +288,7 @@ void silc_stack_free(SilcStack stack) 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); @@ -350,6 +373,8 @@ void *silc_stack_malloc(SilcStack stack, SilcUInt32 size) 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; } @@ -382,6 +407,8 @@ void *silc_stack_malloc(SilcStack stack, SilcUInt32 size) 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; } @@ -394,6 +421,8 @@ void *silc_stack_malloc(SilcStack stack, SilcUInt32 size) 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; @@ -439,6 +468,8 @@ void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size, 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; } @@ -472,6 +503,16 @@ void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size, 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) diff --git a/lib/silcutil/silcstack.h b/lib/silcutil/silcstack.h index 8003666f..c60cd7b7 100644 --- a/lib/silcutil/silcstack.h +++ b/lib/silcutil/silcstack.h @@ -53,9 +53,10 @@ * 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. * ***/ @@ -96,6 +97,22 @@ typedef struct SilcStackStruct *SilcStack; ***/ 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 @@ -108,13 +125,13 @@ typedef struct SilcStackFrameStruct SilcStackFrame; * 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. @@ -134,7 +151,7 @@ SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent); * 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); @@ -153,19 +170,15 @@ 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 @@ -173,7 +186,8 @@ void silc_stack_free(SilcStack stack); * (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 * @@ -210,8 +224,8 @@ SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame); * * 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 @@ -221,7 +235,8 @@ SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame); * 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 * @@ -246,7 +261,7 @@ SilcUInt32 silc_stack_pop(SilcStack stack); * * 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 @@ -255,7 +270,8 @@ SilcUInt32 silc_stack_pop(SilcStack stack); * 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); @@ -273,18 +289,42 @@ 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 @@ -296,7 +336,7 @@ void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size, * 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 diff --git a/lib/silcutil/silcstack_i.h b/lib/silcutil/silcstack_i.h index 03bc8986..71280050 100644 --- a/lib/silcutil/silcstack_i.h +++ b/lib/silcutil/silcstack_i.h @@ -25,7 +25,7 @@ #endif /* The default stack size when stack is created */ -#define SILC_STACK_DEFAULT_SIZE 2048 +#define SILC_STACK_DEFAULT_SIZE 1024 /* Number of pre-allocated stack frames. Frames are allocated from the stack itself. */ @@ -35,7 +35,7 @@ #define SILC_STACK_DEFAULT_ALIGN sizeof(unsigned long) /* Maximum allocation that can be made with SilcStack. */ -#define SILC_STACK_BLOCK_NUM 20 +#define SILC_STACK_BLOCK_NUM 21 #define SILC_STACK_MAX_ALLOC \ (SILC_STACK_DEFAULT_SIZE * (1L << (SILC_STACK_BLOCK_NUM - 1)) << 1) -- 2.24.0