Added SILC Thread Queue API
[silc.git] / lib / silcutil / silcstack.c
index 7f179eb4e5c4ca5c7a516985f1ac20445ee5072a..3fd06d331459680cefa66328ccdb735500c6f866 100644 (file)
@@ -4,7 +4,7 @@
 
   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
@@ -75,7 +78,10 @@ static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack,
   *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);
@@ -85,9 +91,16 @@ 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;
   }
 
+  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 */
@@ -105,6 +118,7 @@ static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack,
   e->bsize = bsize;
 
   SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
+
   return e;
 }
 
@@ -127,7 +141,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 */
@@ -161,14 +177,18 @@ SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
   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);
@@ -178,10 +198,13 @@ SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
     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);
@@ -229,6 +252,9 @@ SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
       silc_free(stack);
       return NULL;
     }
+
+    /* Allocate lock */
+    silc_mutex_alloc(&stack->lock);
   }
 
   /* Use the allocated stack in first stack frame */
@@ -250,6 +276,9 @@ void silc_stack_free(SilcStack stack)
   SilcStackDataEntry e;
   int i;
 
+  if (!stack)
+    return;
+
   SILC_LOG_DEBUG(("Free stack %p", stack));
 
   if (!stack->parent) {
@@ -264,8 +293,12 @@ void silc_stack_free(SilcStack stack)
       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);
@@ -342,14 +375,18 @@ void *silc_stack_malloc(SilcStack stack, SilcUInt32 size)
   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;
   }
 
@@ -380,8 +417,11 @@ void *silc_stack_malloc(SilcStack stack, SilcUInt32 size)
     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;
   }
 
@@ -394,6 +434,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;
@@ -431,14 +473,18 @@ void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
   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;
   }
 
@@ -454,6 +500,7 @@ void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
   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;
   }
@@ -468,10 +515,22 @@ void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
   }
 
   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)
@@ -487,6 +546,92 @@ SilcUInt32 silc_stack_get_alignment(SilcStack stack)
   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. */