Added multi-thread support. Added OOM handler.
authorPekka Riikonen <priikone@silcnet.org>
Wed, 4 Jul 2007 13:29:37 +0000 (13:29 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 4 Jul 2007 13:29:37 +0000 (13:29 +0000)
lib/silcutil/silcstack.c
lib/silcutil/silcstack.h
lib/silcutil/silcstack_i.h

index 7f179eb4e5c4ca5c7a516985f1ac20445ee5072a..e314d8d09ad43ee779d4f39d3151b56724ec8433 100644 (file)
 /* 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)
index 8003666f9d9ff12478914d0c681cdcb9f8f07179..c60cd7b7221b76b92855b49ffaa805c39edc6336 100644 (file)
  * 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
index 03bc8986dab2a37ca221d3e2cbd437d36ef9076c..712800502762421b50927e2c5ca8fde2ab6e6b90 100644 (file)
@@ -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)