Added reference counting the SilcStack.
[runtime.git] / lib / silcutil / silcstack.c
index 02e2e0c203b145a72ad1f3e3153afdefd4af335e..576ef6198c4b7645a91069840e2934413dc718a6 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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silc.h"
+#include "silcruntime.h"
 
 /************************** Types and definitions ***************************/
 
@@ -33,6 +33,7 @@ struct SilcStackStruct {
   void *oom_context;                         /* OOM handler context */
   SilcUInt32 stack_size;                     /* Default stack size */
   SilcUInt32 alignment;                              /* Memory alignment */
+  SilcAtomic32 refcnt;                       /* Reference counter */
 #ifdef SILC_DIST_INPLACE
   /* Statistics */
   SilcUInt32 snum_malloc;
@@ -177,6 +178,9 @@ 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 (but does not consume parent's own stack). */
@@ -208,6 +212,12 @@ SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
       return NULL;
     }
 
+    /* Reference parent */
+    SILC_LOG_DEBUG(("Reference stack %p, refcnt %d > %d", stack->parent,
+                   silc_atomic_get_int32(&stack->parent->refcnt),
+                   silc_atomic_get_int32(&stack->parent->refcnt) + 1));
+    silc_atomic_add_int32(&stack->parent->refcnt, 1);
+
     /* Set the initial stack */
     stack->stack = e;
   } else {
@@ -254,6 +264,8 @@ SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
     silc_mutex_alloc(&stack->lock);
   }
 
+  silc_atomic_init32(&stack->refcnt, 1);
+
   /* Use the allocated stack in first stack frame */
   stack->frame = &stack->frames[0];
   stack->frame->prev = NULL;
@@ -276,7 +288,13 @@ void silc_stack_free(SilcStack stack)
   if (!stack)
     return;
 
-  SILC_LOG_DEBUG(("Free stack %p", stack));
+  SILC_LOG_DEBUG(("Free stack %p, refcnt %d > %d", stack,
+                 silc_atomic_get_int32(&stack->refcnt),
+                 silc_atomic_get_int32(&stack->refcnt) - 1));
+
+  /* Unreference */
+  if (silc_atomic_sub_int32(&stack->refcnt, 1) > 0)
+    return;
 
   if (!stack->parent) {
     silc_list_start(stack->stacks);
@@ -293,6 +311,8 @@ void silc_stack_free(SilcStack stack)
     if (stack->lock)
       silc_mutex_free(stack->lock);
 
+    silc_atomic_uninit32(&stack->refcnt);
+
     silc_free(stack);
   } else {
     /* Return all stack blocks to the parent */
@@ -301,6 +321,9 @@ void silc_stack_free(SilcStack stack)
       silc_stack_unref_stack(stack->parent, e);
 
     silc_stack_unref_stack(stack->parent, stack->stack);
+
+    /* Unreference parent */
+    silc_stack_free(stack->parent);
   }
 }
 
@@ -372,13 +395,15 @@ 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);
@@ -412,7 +437,8 @@ 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);
@@ -467,13 +493,15 @@ 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);
@@ -492,6 +520,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;
   }
@@ -506,6 +535,8 @@ 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;
 }
@@ -592,6 +623,35 @@ SilcBool silc_stack_purge(SilcStack stack)
   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. */