You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@apr.apache.org by st...@apache.org on 2001/07/12 18:50:23 UTC

cvs commit: apr/memory/unix apr_sms_threads.c Makefile.in

striker     01/07/12 09:50:22

  Modified:    memory/unix Makefile.in
  Added:       include  apr_sms_threads.h
               memory/unix apr_sms_threads.c
  Log:
  Add a SMS module that will maintain a free list per thread. This could
  be usefull when avoiding locking across threads.
  
  Revision  Changes    Path
  1.1                  apr/include/apr_sms_threads.h
  
  Index: apr_sms_threads.h
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  #ifndef APR_SMS_THREADS_H
  #define APR_SMS_THREADS_H
  
  #include "apr.h"
  #include "apr_sms.h"
  
  #if APR_HAS_THREADS
   
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /**
   * @package APR threads memory system
   */
  
  /**
   * Create a pool like malloc/free/reset memory system
   * @param mem_sys A pointer to the returned apr_sms_t*
   * @param pms The parent memory system, used to allocate the memory
   *            that we will be threads.
   * @deffunc apr_status_t apr_sms_threads_create(apr_sms_t **mem_sys,
   *                                               apr_sms_t *pms);
   */
  APR_DECLARE(apr_status_t) apr_sms_threads_create(apr_sms_t **sms,
                                                    apr_sms_t *pms);
  
  /**
   * Create a pool like malloc/free/reset memory system
   * @param mem_sys A pointer to the returned apr_sms_t*
   * @param pms The parent memory system, used to allocate the memory
   *            that we will be threads.
   * @param min_alloc The minimal blocksize to allocate when getting
   *                  memory from the parent
   * @param min_free  The minimal amount of memory to make sure is
   *                  free in an allocated block from the parent 
   * @param max_free  The amount of memory the sms may hold on to
   * @deffunc apr_status_t apr_sms_threads_create_ex(apr_sms_t **mem_sys,
   *                                                 apr_sms_t *pms,
   *                                                 apr_size_t min_alloc,
   *                                                 apr_size_t min_free,
   *                                                 apr_size_t max_free);
   */
  APR_DECLARE(apr_status_t) apr_sms_threads_create_ex(apr_sms_t **sms,
                                                      apr_sms_t *pms,
                                                      apr_size_t min_alloc,
                                                      apr_size_t min_free,
                                                      apr_size_t max_free);
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif /* APR_HAS_THREADS */
  
  #endif /* !APR_SMS_THREADS_H */
  
  
  
  1.7       +2 -1      apr/memory/unix/Makefile.in
  
  Index: Makefile.in
  ===================================================================
  RCS file: /home/cvs/apr/memory/unix/Makefile.in,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- Makefile.in	2001/07/07 07:17:32	1.6
  +++ Makefile.in	2001/07/12 16:50:19	1.7
  @@ -4,7 +4,8 @@
             apr_sms_tracking.lo \
             apr_sms_blocks.lo \
             apr_sms_trivial.lo \
  -          apr_pools.lo
  +          apr_sms_threads.lo \
  +          @POOLS_TARGET@
   
   # bring in rules.mk for standard functionality
   @INCLUDE_RULES@
  
  
  
  1.1                  apr/memory/unix/apr_sms_threads.c
  
  Index: apr_sms_threads.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  #include "apr.h"
  #include "apr_general.h"
  #include "apr_private.h"
  #include "apr_sms.h"
  #include "apr_sms_threads.h"
  #include "apr_lock.h"
  #include "sms_private.h"
  
  #if APR_HAS_THREADS
   
  static const char *module_identity = "THREADS";
  static const char *module_acct_identity = "THREADS_ACCT";
  
  /*
   * Simple thread memory system
   */
  
  /* INTERNALLY USED STRUCTURES */
  
  typedef struct block_t
  {
      struct node_t  *node;
  } block_t;
  
  typedef struct node_t
  {
      struct node_t  *next;
      struct node_t  *prev;
      char           *first_avail;
      apr_size_t      avail_size;
      apr_uint16_t    count;
  } node_t;
  
  typedef struct thread_node_t
  {
      struct thread_node_t  *next;
      struct thread_node_t **ref;
      apr_os_thread_t        thread;
      node_t                 used_sentinel;
      node_t                 free_sentinel;
      node_t                *self;
      apr_size_t             max_free;
  } thread_node_t;
  
  /*
   * Primes just below a power of 2:
   * 3, 7, 13, 31, 61, 127, 251, 509, 1021,
   * 2039, 4093, 8191, 16381, 32749, 65521
   */
  
  #define HASH_SIZE 1021
  #define SIZEOF_HASHTABLE APR_ALIGN_DEFAULT(HASH_SIZE)
  
  typedef struct apr_sms_threads_t
  {
      apr_sms_t            sms_hdr;
      node_t               used_sentinel;
      node_t               free_sentinel;
      node_t              *self;
      char                *first_avail;
      thread_node_t       *hashtable[SIZEOF_HASHTABLE];
      apr_size_t           min_free;
      apr_size_t           min_alloc;
      apr_size_t           max_free;
      apr_size_t           thread_max_free;
      apr_lock_t          *lock;
  } apr_sms_threads_t;
  
  typedef struct apr_sms_threads_acct_t
  {
      apr_sms_t            sms_hdr;
      apr_sms_threads_t   *tms;
  } apr_sms_threads_acct_t;
  
  #define SIZEOF_BLOCK_T       APR_ALIGN_DEFAULT(sizeof(block_t))
  #define SIZEOF_NODE_T        APR_ALIGN_DEFAULT(sizeof(node_t))
  #define SIZEOF_THREAD_NODE_T APR_ALIGN_DEFAULT(sizeof(thread_node_t))
  #define SIZEOF_SMS_THREADS_T APR_ALIGN_DEFAULT(sizeof(apr_sms_threads_t))
  #define SIZEOF_SMS_ACCT_T    APR_ALIGN_DEFAULT(sizeof(apr_sms_threads_acct_t))
  
  #define BLOCK_T(mem)         ((block_t *)(mem))
  #define NODE_T(mem)          ((node_t *)(mem))
  #define THREAD_NODE_T(mem)   ((thread_node_t *)(mem))
  #define SMS_THREADS_T(sms)   ((apr_sms_threads_t *)(sms))
  #define SMS_ACCT_T(sms)      ((apr_sms_threads_acct_t *)(sms))
  
  /* Magic numbers :) */
  
  #define MIN_ALLOC       0x2000
  #define MIN_FREE        0x1000
  #define MAX_FREE        0x100000
  #define THREAD_MAX_FREE 0x80000
  
  static void *apr_sms_threads_malloc(apr_sms_t *sms,
                                      apr_size_t size)
  {
      thread_node_t *thread_node;
      apr_os_thread_t thread;
      node_t *node, *sentinel;
      apr_size_t node_size;
      apr_uint16_t hash;
      void *mem;
  
      /* Round up the size to the next 8 byte boundary */
      size = APR_ALIGN_DEFAULT(size) + SIZEOF_BLOCK_T;
  
      thread = apr_os_thread_current();
      hash = thread % HASH_SIZE;
     
      /* If the thread wasn't registered before, we will segfault */ 
      thread_node = SMS_THREADS_T(sms)->hashtable[hash];
      while (thread_node->thread != thread)
          thread_node = thread_node->next;
     
      node = thread_node->used_sentinel.prev;
  
      if (node->avail_size >= size) {
          mem = node->first_avail;
          node->avail_size -= size;
          node->first_avail += size;
          node->count++;
  
          BLOCK_T(mem)->node = node;
          mem = (char *)mem + SIZEOF_BLOCK_T;
  
          return mem;
      }
  
      /* reset the 'last' block, it will be replaced soon */
      node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
  
      /* browse the free list for a useable block */
      sentinel = &thread_node->free_sentinel;
      sentinel->avail_size = size;
  
      node = sentinel->next;
      while (node->avail_size < size)
          node = node->next;
  
      if (node != sentinel) {
          node->prev->next = node->next;
          node->next->prev = node->prev;
  
          sentinel = &thread_node->used_sentinel;
          node->prev = sentinel->prev;
          node->prev->next = node;
          node->next = sentinel;
          sentinel->prev = node;
          
          if (node != thread_node->self)
              thread_node->max_free += node->avail_size;
          
          mem = node->first_avail;
          node->avail_size  -= size;
          node->first_avail += size;
          node->count = 1;
  
          BLOCK_T(mem)->node = node;
          mem = (char *)mem + SIZEOF_BLOCK_T;
  
          return mem;
      }
      
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_acquire(SMS_THREADS_T(sms)->lock);
      
      /* browse the shared free list for a useable block */
      sentinel = &SMS_THREADS_T(sms)->free_sentinel;
      sentinel->avail_size = size;
  
      node = sentinel->next;
      while (node->avail_size < size)
          node = node->next;
  
      if (node != sentinel) {
          node->prev->next = node->next;
          node->next->prev = node->prev;
  
          if (SMS_THREADS_T(sms)->lock)
              apr_lock_release(SMS_THREADS_T(sms)->lock);
  
          sentinel = &thread_node->used_sentinel;
          node->prev = sentinel->prev;
          node->prev->next = node;
          node->next = sentinel;
          sentinel->prev = node;
         
          SMS_THREADS_T(sms)->max_free += node->avail_size;
          
          mem = node->first_avail;
          node->avail_size  -= size;
          node->first_avail += size;
          node->count = 1;
  
          BLOCK_T(mem)->node = node;
          mem = (char *)mem + SIZEOF_BLOCK_T;
  
          return mem;
      }
      
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_release(SMS_THREADS_T(sms)->lock);
      
      /* we have to allocate a new block from our parent */
      node_size = size + SMS_THREADS_T(sms)->min_free;
      if (node_size < SMS_THREADS_T(sms)->min_alloc)
          node_size = SMS_THREADS_T(sms)->min_alloc;
      
      node = apr_sms_malloc(sms->parent, node_size);
      if (!node) {
          /* restore the 'last' node to prevent the
           * next allocation from segfaulting
           */
          node = thread_node->used_sentinel.prev;
          node->first_avail += node->avail_size;
          node->avail_size = 0;
          
          return NULL;
      }
  
      sentinel = &thread_node->used_sentinel;
      node->prev = sentinel->prev;
      node->prev->next = node;
      node->next = sentinel;
      sentinel->prev = node;
      
      mem = node->first_avail = (char *)node + SIZEOF_NODE_T;
      node->first_avail += size;
      node->avail_size = node_size - (node->first_avail - (char *)node);
      node->count = 1;
  
      BLOCK_T(mem)->node = node;
      mem = (char *)mem + SIZEOF_BLOCK_T;
      
      return mem;
  }
  
  static apr_status_t apr_sms_threads_free(apr_sms_t *sms, void *mem)
  {
      node_t *node, *sentinel;
      thread_node_t *thread_node;
      apr_os_thread_t thread;
      apr_uint16_t hash;
  
      node = BLOCK_T((char *)mem - SIZEOF_BLOCK_T)->node;
  
      node->count--;
      if (node->count)
          return APR_SUCCESS;
  
      thread = apr_os_thread_current();
      hash = thread % HASH_SIZE;
      
      thread_node = SMS_THREADS_T(sms)->hashtable[hash];
      while (thread_node->thread != thread)
          thread_node = thread_node->next;
  
      node->avail_size += node->first_avail - 
                          ((char *)node + SIZEOF_NODE_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
     
      if (thread_node->used_sentinel.prev == node)
          return APR_SUCCESS;
  
      node->next->prev = node->prev;
      node->prev->next = node->next;
  
      if (thread_node->max_free >= node->avail_size ||
          node == thread_node->self) {
          sentinel = &thread_node->free_sentinel; 
          node->prev = sentinel->prev;
          node->prev->next = node;
          node->next = sentinel;
          sentinel->prev = node;
  
          if (node != thread_node->self)
              thread_node->max_free -= node->avail_size;
          
          return APR_SUCCESS;
      }
  
      if (sms->parent->free_fn && 
          node->avail_size > SMS_THREADS_T(sms)->max_free)
          return apr_sms_free(sms->parent, node);
  
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_acquire(SMS_THREADS_T(sms)->lock);
      
      sentinel = &SMS_THREADS_T(sms)->free_sentinel;
      node->prev = sentinel->prev;
      node->prev->next = node;
      node->next = sentinel;
      sentinel->prev = node;
  
      SMS_THREADS_T(sms)->max_free -= node->avail_size;
     
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_release(SMS_THREADS_T(sms)->lock);
      
      return APR_SUCCESS;
  }
  
  static apr_status_t apr_sms_threads_thread_register(apr_sms_t *sms,
                                                      apr_os_thread_t thread);
  
  static apr_status_t apr_sms_threads_reset(apr_sms_t *sms)
  {
      node_t *node, *prev, *used_sentinel, *free_sentinel, *free_list;
      thread_node_t *thread_node;
      apr_uint16_t hash;
      apr_size_t min_alloc, max_free;
  
      used_sentinel = &SMS_THREADS_T(sms)->used_sentinel;
      free_sentinel = &SMS_THREADS_T(sms)->free_sentinel;
  
      free_list = NULL;
      
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_acquire(SMS_THREADS_T(sms)->lock);
      
      /* Actually, the thread creation function should have installed
       * a cleanup which effectively kills all the threads created using
       * this sms. Therefor it is not wise to call reset from another
       * thread than the 'master' thread.
       */
      for (hash = 0; hash < HASH_SIZE; hash++) {
          while ((thread_node = SMS_THREADS_T(sms)->hashtable[hash]) != NULL) {
              if ((*thread_node->ref = thread_node->next) != NULL)
                  thread_node->next->ref = thread_node->ref;
  
              node = thread_node->self;
              node->prev->next = node->next;
              node->next->prev = node->prev;
              node->avail_size += node->first_avail - 
                                  ((char *)node + SIZEOF_NODE_T);
  
              node = thread_node->used_sentinel.prev;
              node->avail_size += node->first_avail -
                                  ((char *)node + SIZEOF_NODE_T);
              node->first_avail = (char *)node + SIZEOF_NODE_T;
              node->next = thread_node->free_sentinel.next;
              node->next->prev = node;
  
              node = thread_node->free_sentinel.prev;
              node->next = used_sentinel->next;
              node->next->prev = node;
  
              used_sentinel->next = thread_node->used_sentinel.next;
              used_sentinel->next->prev = used_sentinel;
  
              node = NODE_T(thread_node);
              node->avail_size = thread_node->self->avail_size + 
                                 SIZEOF_THREAD_NODE_T;
              node->first_avail = (char *)node + SIZEOF_NODE_T;
  
              node->next = used_sentinel->next;
              node->next->prev = node;
              used_sentinel->next = node;
              node->prev = used_sentinel;
          }
      }
      
      node = SMS_THREADS_T(sms)->self;
      node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
      node->count = 0;
      node->prev->next = node->next;
      node->next->prev = node->prev;
      
      node = used_sentinel->prev;
      node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
  
      if (sms->parent->free_fn) {
          min_alloc = SMS_THREADS_T(sms)->min_alloc;
          max_free  = SMS_THREADS_T(sms)->max_free;
          used_sentinel->avail_size = min_alloc;
          while (max_free > min_alloc) {
              if (node->avail_size <= max_free) {
                  if (node == used_sentinel)
                      break;
  
                  max_free -= node->avail_size;
                  node->prev->next = node->next;
                  node->next->prev = node->prev;
                  
                  prev = node->prev;
                  
                  node->next = free_sentinel->next;
                  free_sentinel->next = node;
                  node->next->prev = node;
                  node->prev = free_sentinel;
                  
                  node = prev;
              }
              else
                  node = node->prev;
          }
          SMS_THREADS_T(sms)->max_free = max_free;
  
          used_sentinel->prev->next = NULL;
          free_list = used_sentinel->next;
      }
      else {
          node = used_sentinel->prev;
          node->next = free_sentinel->next;
          node->next->prev = node;
  
          node = used_sentinel->next;
          node->prev = free_sentinel;
          free_sentinel->next = node;
      }
      
      node = SMS_THREADS_T(sms)->self;
      node->next = node->prev = used_sentinel;
      used_sentinel->next = used_sentinel->prev = node;
  
      sms->accounting = (apr_sms_t *)((char *)sms + SIZEOF_SMS_THREADS_T);
      
      apr_sms_threads_thread_register(sms, apr_os_thread_current());
  
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_release(SMS_THREADS_T(sms)->lock);
  
      while ((node = free_list) != NULL) {
          free_list = node->next;
          apr_sms_free(sms->parent, node);
      }
      
      return APR_SUCCESS;    
  }
  
  static apr_status_t apr_sms_threads_pre_destroy(apr_sms_t *sms)
  {
      /* This function WILL always be called.  However, be aware that the
       * main sms destroy function knows that it's not wise to try and destroy
       * the same piece of memory twice, so the destroy function in a child won't
       * neccesarily be called.  To guarantee we destroy the lock it's therefore
       * destroyed here.
       */
          
      if (SMS_THREADS_T(sms)->lock) {
          apr_lock_acquire(SMS_THREADS_T(sms)->lock);
          apr_lock_destroy(SMS_THREADS_T(sms)->lock);
          SMS_THREADS_T(sms)->lock = NULL;
      }
      
      return APR_SUCCESS;    
  }
  
  static apr_status_t apr_sms_threads_destroy(apr_sms_t *sms)
  {
      node_t *node, *next, *used_sentinel, *free_sentinel;
      thread_node_t *thread_node;
      apr_uint16_t hash;
  
      for (hash = 0; hash < HASH_SIZE; hash++) {
          while ((thread_node = SMS_THREADS_T(sms)->hashtable[hash]) != NULL) {
              if ((*thread_node->ref = thread_node->next) != NULL)
                  thread_node->next->ref = thread_node->ref;
  
              node = thread_node->self;
              node->prev->next = node->next;
              node->next->prev = node->prev;
  
              used_sentinel = &thread_node->used_sentinel;
              free_sentinel = &thread_node->free_sentinel;
              
              free_sentinel->prev->next = NULL;
              used_sentinel->prev->next = free_sentinel->next;
  
              node = used_sentinel->next;
              while (node) {
                  next = node->next;
                  apr_sms_free(sms->parent, node);
                  node = next;
              }
  
              apr_sms_free(sms->parent, thread_node);
          }
      }
  
      node = SMS_THREADS_T(sms)->self;
      node->prev->next = node->next;
      node->next->prev = node->prev;
      
      used_sentinel = &SMS_THREADS_T(sms)->used_sentinel;
      free_sentinel = &SMS_THREADS_T(sms)->free_sentinel;
  
      free_sentinel->prev->next = NULL;
      used_sentinel->prev->next = free_sentinel->next;
  
      node = used_sentinel->next;
      while (node) {
          next = node->next;
          apr_sms_free(sms->parent, node);
          node = next;
      }
  
      return apr_sms_free(sms->parent, sms);
  }
  
  static apr_status_t apr_sms_threads_thread_register(apr_sms_t *sms,
                                                      apr_os_thread_t thread)
  {
      thread_node_t *thread_node;
      node_t *node, *sentinel;
      apr_uint16_t hash;
  
      hash = thread % HASH_SIZE;
      
      if (sms->threads > 1 && !SMS_THREADS_T(sms)->lock) {
          apr_lock_create(&SMS_THREADS_T(sms)->lock,
                          APR_MUTEX, APR_LOCKALL,
                          NULL, sms->pool);
      }
      
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_acquire(SMS_THREADS_T(sms)->lock);
  
      sentinel = &SMS_THREADS_T(sms)->free_sentinel;
      if ((node = sentinel->next) != sentinel) {
          node->prev->next = node->next;
          node->next->prev = node->prev;
  
          thread_node = THREAD_NODE_T(node);
          node = NODE_T((char *)thread_node + SIZEOF_THREAD_NODE_T);
          node->avail_size = NODE_T(thread_node)->avail_size - 
                             SIZEOF_THREAD_NODE_T;
          node->first_avail = (char *)node + SIZEOF_NODE_T;
      }
      else {
          thread_node = apr_sms_malloc(sms->parent, SMS_THREADS_T(sms)->min_alloc);
          if (!thread_node) {
              if (SMS_THREADS_T(sms)->lock)
                  apr_lock_release(SMS_THREADS_T(sms)->lock);
  
              return APR_ENOMEM;
          }
  
          node = NODE_T((char *)thread_node + SIZEOF_THREAD_NODE_T);
          node->first_avail = (char *)node + SIZEOF_NODE_T;
          node->avail_size = SMS_THREADS_T(sms)->min_alloc -
                             (node->first_avail - (char *)thread_node);
      }
      node->count = 0;
      
      thread_node->self = node;
      thread_node->max_free = SMS_THREADS_T(sms)->thread_max_free;
  
      sentinel = &thread_node->used_sentinel;
      node->prev = node->next = sentinel;
      sentinel->prev = sentinel->next = node;
  
      sentinel = &thread_node->free_sentinel;
      sentinel->prev = sentinel->next = sentinel;
  
      thread_node->thread = thread;
      if ((thread_node->next = SMS_THREADS_T(sms)->hashtable[hash]) != NULL)
          thread_node->next->ref = &thread_node->next;
      thread_node->ref = &SMS_THREADS_T(sms)->hashtable[hash];
      SMS_THREADS_T(sms)->hashtable[hash] = thread_node;
  
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_release(SMS_THREADS_T(sms)->lock);
  
      return APR_SUCCESS;
  }
  
  static apr_status_t apr_sms_threads_thread_unregister(apr_sms_t *sms,
                                                        apr_os_thread_t thread)
  {
      thread_node_t *thread_node;
      node_t *node, *prev, *free_list, *used_sentinel, *free_sentinel;
      apr_uint16_t hash;
      apr_size_t min_alloc, max_free;
  
      hash = thread % HASH_SIZE;
      free_list = NULL;
      
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_acquire(SMS_THREADS_T(sms)->lock);
  
      thread_node = SMS_THREADS_T(sms)->hashtable[hash];
      while (thread_node->thread != thread)
          thread_node = thread_node->next;
  
      if ((*thread_node->ref = thread_node->next) != NULL)
          thread_node->next->ref = thread_node->ref;
  
      node = thread_node->self;
      node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
      node->prev->next = node->next;
      node->next->prev = node->prev;
      
      used_sentinel = &thread_node->used_sentinel;
      free_sentinel = &SMS_THREADS_T(sms)->free_sentinel;
      
      node = used_sentinel->prev;
      node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
     
      node->next = thread_node->free_sentinel.next;
      node->next->prev = node;
  
      node = thread_node->free_sentinel.prev;
      node->next = used_sentinel;
      used_sentinel->prev = node;
  
      min_alloc = SMS_THREADS_T(sms)->min_alloc;
      max_free  = SMS_THREADS_T(sms)->max_free;
      used_sentinel->avail_size = min_alloc;
      while (max_free > min_alloc) {
          if (node->avail_size <= max_free) {
              if (node == used_sentinel)
                  break;
      
              max_free -= node->avail_size;
              node->prev->next = node->next;
              node->next->prev = node->prev;
              prev = node->prev;
  
              node->next = free_sentinel->next;
              free_sentinel->next = node;
              node->next->prev = node;
              node->prev = free_sentinel;
              
              node = prev;
          }
          else
              node = node->prev;
      }
                              
      used_sentinel->prev->next = NULL;
      free_list = used_sentinel->next;
                      
      node = NODE_T(thread_node);
      node->avail_size = thread_node->self->avail_size + 
                         SIZEOF_THREAD_NODE_T;
      node->first_avail = (char *)node + SIZEOF_NODE_T;
  
      if (max_free >= node->avail_size) {
          max_free -= node->avail_size;
  
          node->next = free_sentinel->next;
          node->next->prev = node;
          free_sentinel->next = node;
          node->prev = free_sentinel;
      }
      else {
          node->next = free_list;
          free_list = node;
      }
      
      SMS_THREADS_T(sms)->max_free = max_free;
      
      if (SMS_THREADS_T(sms)->lock)
          apr_lock_release(SMS_THREADS_T(sms)->lock);
  
      while ((node = free_list) != NULL) {
          free_list = node->next;
          apr_sms_free(sms->parent, node);
      }
      
      return APR_SUCCESS;
  }
  
  static void *apr_sms_threads_acct_malloc(apr_sms_t *sms, apr_size_t size)
  {
      apr_sms_threads_t *tms;
      node_t *node, *sentinel;
      apr_size_t node_size;
      void *mem;
  
      tms = SMS_ACCT_T(sms)->tms;
  
      /* Round up the size to the next 8 byte boundary */
      size = APR_ALIGN_DEFAULT(size) + SIZEOF_BLOCK_T;
  
      if (tms->lock)
          apr_lock_acquire(tms->lock);
      
      node = tms->used_sentinel.prev;
  
      if (node->avail_size >= size) {
          mem = node->first_avail;
          node->avail_size -= size;
          node->first_avail += size;
          node->count++;
  
          if (tms->lock)
              apr_lock_release(tms->lock);
      
          BLOCK_T(mem)->node = node;
          mem = (char *)mem + SIZEOF_BLOCK_T;
  
          return mem;
      }
  
      /* reset the 'last' block, it will be replaced soon */
      node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
  
      /* browse the free list for a useable block */
      sentinel = &tms->free_sentinel;
      sentinel->avail_size = size;
  
      node = sentinel->next;
      while (node->avail_size < size)
          node = node->next;
  
      if (node != sentinel) {
          node->prev->next = node->next;
          node->next->prev = node->prev;
  
          sentinel = &tms->used_sentinel;
          node->prev = sentinel->prev;
          node->prev->next = node;
          node->next = sentinel;
          sentinel->prev = node;
          
          if (node != tms->self)
              tms->max_free += node->avail_size;
  
          mem = node->first_avail;
          node->avail_size  -= size;
          node->first_avail += size;
          node->count = 1;
  
          if (tms->lock)
              apr_lock_release(tms->lock);
  
          BLOCK_T(mem)->node = node;
          mem = (char *)mem + SIZEOF_BLOCK_T;
  
          return mem;
      }
      
      /* we have to allocate a new block from our parent */
      node_size = size + tms->min_free;
      if (node_size < tms->min_alloc)
          node_size = tms->min_alloc;
      
      node = apr_sms_malloc(sms->parent, node_size);
      if (!node) {
          /* restore the 'last' node, so the next allocation
           * will not segfault
           */
          node = tms->used_sentinel.prev;
          node->first_avail += node->avail_size;
          node->avail_size = 0;
  
          if (tms->lock)
              apr_lock_release(tms->lock);
          
          return NULL;
      }
  
      sentinel = &tms->used_sentinel;
      node->prev = sentinel->prev;
      node->prev->next = node;
      node->next = sentinel;
      sentinel->prev = node;
      
      mem = node->first_avail = (char *)node + SIZEOF_NODE_T;
      node->first_avail += size;
      node->avail_size = node_size - (node->first_avail - (char *)node);
      node->count = 1;
  
      if (tms->lock)
          apr_lock_release(tms->lock);
  
      BLOCK_T(mem)->node = node;
      mem = (char *)mem + SIZEOF_BLOCK_T;
      
      return mem;
  }
  
  static apr_status_t apr_sms_threads_acct_free(apr_sms_t *sms, void *mem)
  {
      apr_sms_threads_t *tms;
      node_t *node, *sentinel;
  
      tms = SMS_ACCT_T(sms)->tms;
      node = BLOCK_T((char *)mem - SIZEOF_BLOCK_T)->node;
  
      if (tms->lock)
          apr_lock_acquire(tms->lock);
  
      node->count--;
  
      if (node->count) {
          if (tms->lock)
              apr_lock_release(tms->lock);
  
          return APR_SUCCESS;
      }
  
      node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
      
      if (tms->used_sentinel.prev != node) {
          node->next->prev = node->prev;
          node->prev->next = node->next;
  
          if (sms->parent->free_fn &&
              node->avail_size > tms->max_free &&
              node != tms->self) {
              if (tms->lock)
                  apr_lock_release(tms->lock);
  
              return apr_sms_free(sms->parent, node);
          }
          
          sentinel = &tms->free_sentinel;
          node->prev = sentinel->prev;
          node->prev->next = node;
          node->next = sentinel;
          sentinel->prev = node;
  
          if (node != tms->self)
              tms->max_free -= node->avail_size;
      }
  
      if (tms->lock)
          apr_lock_release(tms->lock);
  
      return APR_SUCCESS;
  }
  
  APR_DECLARE(apr_status_t) apr_sms_threads_create(apr_sms_t **sms, 
                                                   apr_sms_t *pms)
  {
      return apr_sms_threads_create_ex(sms, pms, MIN_ALLOC, MIN_FREE, MAX_FREE);    
  }
  
  APR_DECLARE(apr_status_t) apr_sms_threads_create_ex(apr_sms_t **sms, 
                                                      apr_sms_t *pms,
                                                      apr_size_t min_alloc,
                                                      apr_size_t min_free,
                                                      apr_size_t max_free)
  {
      apr_sms_t *new_sms, *ams;
      apr_sms_threads_t *tms;
      node_t *node;
      apr_status_t rv;
  
      *sms = NULL;
  
      min_alloc = APR_ALIGN_DEFAULT(min_alloc);
      min_free  = APR_ALIGN_DEFAULT(min_free);
      
      /* We're not a top level module, ie we have a parent, so
       * we allocate the memory for the structure from our parent.
       * This is safe as we shouldn't outlive our parent...
       */
      new_sms = apr_sms_calloc(pms, min_alloc);
      if (!new_sms)
          return APR_ENOMEM;
  
      if ((rv = apr_sms_init(new_sms, pms)) != APR_SUCCESS)
          return rv;
  
      new_sms->malloc_fn            = apr_sms_threads_malloc;
      new_sms->free_fn              = apr_sms_threads_free;
      new_sms->reset_fn             = apr_sms_threads_reset;
      new_sms->pre_destroy_fn       = apr_sms_threads_pre_destroy;
      new_sms->destroy_fn           = apr_sms_threads_destroy;
      new_sms->thread_register_fn   = apr_sms_threads_thread_register;
      new_sms->thread_unregister_fn = apr_sms_threads_thread_unregister;
      new_sms->identity             = module_identity;
  
      ams = (apr_sms_t *)((char *)new_sms + SIZEOF_SMS_THREADS_T);
      
      node = NODE_T((char *)ams + SIZEOF_SMS_ACCT_T);
      node->first_avail = (char *)node + SIZEOF_NODE_T;
      node->avail_size  = min_alloc - (node->first_avail - (char *)new_sms);
      node->count       = 0;
      
      tms = SMS_THREADS_T(new_sms);
      tms->min_alloc       = min_alloc;
      tms->min_free        = min_free;
      tms->max_free        = max_free;
      tms->thread_max_free = THREAD_MAX_FREE;
      tms->self            = node;
      
      node->next = node->prev = &tms->used_sentinel;
      tms->used_sentinel.next = tms->used_sentinel.prev = node;
      tms->free_sentinel.next = tms->free_sentinel.prev = &tms->free_sentinel;
  
      apr_sms_init(ams, new_sms);
      ams->malloc_fn = apr_sms_threads_acct_malloc;
      ams->free_fn   = apr_sms_threads_acct_free;
      ams->identity  = module_acct_identity;
      SMS_ACCT_T(ams)->tms = tms;
      apr_sms_post_init(ams);
      
      new_sms->accounting = ams;
      
      apr_sms_post_init(new_sms);
      
      *sms = new_sms;
      return APR_SUCCESS;
  }
  
  #endif /* APR_HAS_THREADS */
  
  
  
  

Re: SMS Factory Re: cvs commit: apr/memory/unix apr_sms_threads.cMakefile.in

Posted by Luke Kenneth Casson Leighton <lk...@samba-tng.org>.
On Fri, Jul 13, 2001 at 02:53:59AM +0200, Sander Striker wrote:
> > would it be usefull to have some kind of 
> > 'sms_factory' which you could call
> > either with a identifier, or better yet, a set of capabilities it
> > requires from the SMS
> > (manysmallallocs, realeaseatend ) or ( bigallocs,tracking)
> > 
> > which could then give you the best match for your environment?
> 
> Well, at that point you already know your environment, so we might
> as well create a small tool asking questions about environments.
> Something like so:
> 
> Do you have lots of small allocations [y/n]? y
> Do you free your malloced blocks      [y/n]? n
> ...
> 
> The best probable match is: <type>
> 
> Where type is "std", "tracking", "trivial", "blocks", etc.
> 
> Then you just put in a call to apr_sms_<type>_create on the spot.
> Each type also may have different parameters you can give it
> at create and a factory would make that more difficult.

this fits in well with having a bit-field in every SMS
[a new function]: what features do you support?

see other-post, 30mins ago.

luke

Re: SMS Factory Re: cvs commit: apr/memory/unix apr_sms_threads.c Makefile.in

Posted by Luke Kenneth Casson Leighton <lk...@samba-tng.org>.
On Thu, Jul 12, 2001 at 11:19:44AM -0700, Ian Holsman wrote:
> would it be usefull to have some kind of 
> 'sms_factory' which you could call
> either with a identifier, or better yet, a set of capabilities it
> requires from the SMS
> (manysmallallocs, realeaseatend ) or ( bigallocs,tracking)

[*duur* - i'm re-suggesting what you suggested 10 days ago
what i suggested 40 days ago, by having so many email messages
and them being in first-in, last-out viewing order]

RE: SMS Factory Re: cvs commit: apr/memory/unix apr_sms_threads.cMakefile.in

Posted by Sander Striker <st...@apache.org>.
> would it be usefull to have some kind of 
> 'sms_factory' which you could call
> either with a identifier, or better yet, a set of capabilities it
> requires from the SMS
> (manysmallallocs, realeaseatend ) or ( bigallocs,tracking)
> 
> which could then give you the best match for your environment?

Well, at that point you already know your environment, so we might
as well create a small tool asking questions about environments.
Something like so:

Do you have lots of small allocations [y/n]? y
Do you free your malloced blocks      [y/n]? n
...

The best probable match is: <type>

Where type is "std", "tracking", "trivial", "blocks", etc.

Then you just put in a call to apr_sms_<type>_create on the spot.
Each type also may have different parameters you can give it
at create and a factory would make that more difficult.
 
> so for example the http code would, at worker creation, ask for a SMS
> (or two) and the factory would give it the best it could taking
> threading and type of allocations the caller wanted?
> 
> the idea behind this would be that you would only call this once /twice
> at startup (as it's children would be of the same type in most cases) ,
> so it shouldn't be too much of a performance hit

If it isn't dynamic, in the sense that you always create the same
type of sms given the same environment specs, it is unneccessary overhead.

Just my take on it,

Sander


SMS Factory Re: cvs commit: apr/memory/unix apr_sms_threads.c Makefile.in

Posted by Ian Holsman <ia...@cnet.com>.
would it be usefull to have some kind of 
'sms_factory' which you could call
either with a identifier, or better yet, a set of capabilities it
requires from the SMS
(manysmallallocs, realeaseatend ) or ( bigallocs,tracking)

which could then give you the best match for your environment?

so for example the http code would, at worker creation, ask for a SMS
(or two) and the factory would give it the best it could taking
threading and type of allocations the caller wanted?

the idea behind this would be that you would only call this once /twice
at startup (as it's children would be of the same type in most cases) ,
so it shouldn't be too much of a performance hit



On 12 Jul 2001 16:50:23 +0000, striker@apache.org wrote:
> striker     01/07/12 09:50:22
> 
>   Modified:    memory/unix Makefile.in
>   Added:       include  apr_sms_threads.h
>                memory/unix apr_sms_threads.c
>   Log:
>   Add a SMS module that will maintain a free list per thread. This could
>   be usefull when avoiding locking across threads.
>   
>   Revision  Changes    Path
>   1.1                  apr/include/apr_sms_threads.h
>   
>   Index: apr_sms_threads.h
>   ===================================================================
>   /* ====================================================================
>    * The Apache Software License, Version 1.1
>    *
>    * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
>    * reserved.
>    *
>    * Redistribution and use in source and binary forms, with or without
>    * modification, are permitted provided that the following conditions
>    * are met:
>    *
>    * 1. Redistributions of source code must retain the above copyright
>    *    notice, this list of conditions and the following disclaimer.
>    *
>    * 2. Redistributions in binary form must reproduce the above copyright
>    *    notice, this list of conditions and the following disclaimer in
>    *    the documentation and/or other materials provided with the
>    *    distribution.
>    *
>    * 3. The end-user documentation included with the redistribution,
>    *    if any, must include the following acknowledgment:
>    *       "This product includes software developed by the
>    *        Apache Software Foundation (http://www.apache.org/)."
>    *    Alternately, this acknowledgment may appear in the software itself,
>    *    if and wherever such third-party acknowledgments normally appear.
>    *
>    * 4. The names "Apache" and "Apache Software Foundation" must
>    *    not be used to endorse or promote products derived from this
>    *    software without prior written permission. For written
>    *    permission, please contact apache@apache.org.
>    *
>    * 5. Products derived from this software may not be called "Apache",
>    *    nor may "Apache" appear in their name, without prior written
>    *    permission of the Apache Software Foundation.
>    *
>    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
>    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>    * SUCH DAMAGE.
>    * ====================================================================
>    *
>    * This software consists of voluntary contributions made by many
>    * individuals on behalf of the Apache Software Foundation.  For more
>    * information on the Apache Software Foundation, please see
>    * <http://www.apache.org/>.
>    */
>   
>   #ifndef APR_SMS_THREADS_H
>   #define APR_SMS_THREADS_H
>   
>   #include "apr.h"
>   #include "apr_sms.h"
>   
>   #if APR_HAS_THREADS
>    
>   #ifdef __cplusplus
>   extern "C" {
>   #endif
>   
>   /**
>    * @package APR threads memory system
>    */
>   
>   /**
>    * Create a pool like malloc/free/reset memory system
>    * @param mem_sys A pointer to the returned apr_sms_t*
>    * @param pms The parent memory system, used to allocate the memory
>    *            that we will be threads.
>    * @deffunc apr_status_t apr_sms_threads_create(apr_sms_t **mem_sys,
>    *                                               apr_sms_t *pms);
>    */
>   APR_DECLARE(apr_status_t) apr_sms_threads_create(apr_sms_t **sms,
>                                                     apr_sms_t *pms);
>   
>   /**
>    * Create a pool like malloc/free/reset memory system
>    * @param mem_sys A pointer to the returned apr_sms_t*
>    * @param pms The parent memory system, used to allocate the memory
>    *            that we will be threads.
>    * @param min_alloc The minimal blocksize to allocate when getting
>    *                  memory from the parent
>    * @param min_free  The minimal amount of memory to make sure is
>    *                  free in an allocated block from the parent 
>    * @param max_free  The amount of memory the sms may hold on to
>    * @deffunc apr_status_t apr_sms_threads_create_ex(apr_sms_t **mem_sys,
>    *                                                 apr_sms_t *pms,
>    *                                                 apr_size_t min_alloc,
>    *                                                 apr_size_t min_free,
>    *                                                 apr_size_t max_free);
>    */
>   APR_DECLARE(apr_status_t) apr_sms_threads_create_ex(apr_sms_t **sms,
>                                                       apr_sms_t *pms,
>                                                       apr_size_t min_alloc,
>                                                       apr_size_t min_free,
>                                                       apr_size_t max_free);
>   
>   #ifdef __cplusplus
>   }
>   #endif
>   
>   #endif /* APR_HAS_THREADS */
>   
>   #endif /* !APR_SMS_THREADS_H */
>   
>   
>   
>   1.7       +2 -1      apr/memory/unix/Makefile.in
>   
>   Index: Makefile.in
>   ===================================================================
>   RCS file: /home/cvs/apr/memory/unix/Makefile.in,v
>   retrieving revision 1.6
>   retrieving revision 1.7
>   diff -u -r1.6 -r1.7
>   --- Makefile.in	2001/07/07 07:17:32	1.6
>   +++ Makefile.in	2001/07/12 16:50:19	1.7
>   @@ -4,7 +4,8 @@
>              apr_sms_tracking.lo \
>              apr_sms_blocks.lo \
>              apr_sms_trivial.lo \
>   -          apr_pools.lo
>   +          apr_sms_threads.lo \
>   +          @POOLS_TARGET@
>    
>    # bring in rules.mk for standard functionality
>    @INCLUDE_RULES@
>   
>   
>   
>   1.1                  apr/memory/unix/apr_sms_threads.c
>   
>   Index: apr_sms_threads.c
>   ===================================================================
>   /* ====================================================================
>    * The Apache Software License, Version 1.1
>    *
>    * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
>    * reserved.
>    *
>    * Redistribution and use in source and binary forms, with or without
>    * modification, are permitted provided that the following conditions
>    * are met:
>    *
>    * 1. Redistributions of source code must retain the above copyright
>    *    notice, this list of conditions and the following disclaimer.
>    *
>    * 2. Redistributions in binary form must reproduce the above copyright
>    *    notice, this list of conditions and the following disclaimer in
>    *    the documentation and/or other materials provided with the
>    *    distribution.
>    *
>    * 3. The end-user documentation included with the redistribution,
>    *    if any, must include the following acknowledgment:
>    *       "This product includes software developed by the
>    *        Apache Software Foundation (http://www.apache.org/)."
>    *    Alternately, this acknowledgment may appear in the software itself,
>    *    if and wherever such third-party acknowledgments normally appear.
>    *
>    * 4. The names "Apache" and "Apache Software Foundation" must
>    *    not be used to endorse or promote products derived from this
>    *    software without prior written permission. For written
>    *    permission, please contact apache@apache.org.
>    *
>    * 5. Products derived from this software may not be called "Apache",
>    *    nor may "Apache" appear in their name, without prior written
>    *    permission of the Apache Software Foundation.
>    *
>    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
>    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>    * SUCH DAMAGE.
>    * ====================================================================
>    *
>    * This software consists of voluntary contributions made by many
>    * individuals on behalf of the Apache Software Foundation.  For more
>    * information on the Apache Software Foundation, please see
>    * <http://www.apache.org/>.
>    */
>   
>   #include "apr.h"
>   #include "apr_general.h"
>   #include "apr_private.h"
>   #include "apr_sms.h"
>   #include "apr_sms_threads.h"
>   #include "apr_lock.h"
>   #include "sms_private.h"
>   
>   #if APR_HAS_THREADS
>    
>   static const char *module_identity = "THREADS";
>   static const char *module_acct_identity = "THREADS_ACCT";
>   
>   /*
>    * Simple thread memory system
>    */
>   
>   /* INTERNALLY USED STRUCTURES */
>   
>   typedef struct block_t
>   {
>       struct node_t  *node;
>   } block_t;
>   
>   typedef struct node_t
>   {
>       struct node_t  *next;
>       struct node_t  *prev;
>       char           *first_avail;
>       apr_size_t      avail_size;
>       apr_uint16_t    count;
>   } node_t;
>   
>   typedef struct thread_node_t
>   {
>       struct thread_node_t  *next;
>       struct thread_node_t **ref;
>       apr_os_thread_t        thread;
>       node_t                 used_sentinel;
>       node_t                 free_sentinel;
>       node_t                *self;
>       apr_size_t             max_free;
>   } thread_node_t;
>   
>   /*
>    * Primes just below a power of 2:
>    * 3, 7, 13, 31, 61, 127, 251, 509, 1021,
>    * 2039, 4093, 8191, 16381, 32749, 65521
>    */
>   
>   #define HASH_SIZE 1021
>   #define SIZEOF_HASHTABLE APR_ALIGN_DEFAULT(HASH_SIZE)
>   
>   typedef struct apr_sms_threads_t
>   {
>       apr_sms_t            sms_hdr;
>       node_t               used_sentinel;
>       node_t               free_sentinel;
>       node_t              *self;
>       char                *first_avail;
>       thread_node_t       *hashtable[SIZEOF_HASHTABLE];
>       apr_size_t           min_free;
>       apr_size_t           min_alloc;
>       apr_size_t           max_free;
>       apr_size_t           thread_max_free;
>       apr_lock_t          *lock;
>   } apr_sms_threads_t;
>   
>   typedef struct apr_sms_threads_acct_t
>   {
>       apr_sms_t            sms_hdr;
>       apr_sms_threads_t   *tms;
>   } apr_sms_threads_acct_t;
>   
>   #define SIZEOF_BLOCK_T       APR_ALIGN_DEFAULT(sizeof(block_t))
>   #define SIZEOF_NODE_T        APR_ALIGN_DEFAULT(sizeof(node_t))
>   #define SIZEOF_THREAD_NODE_T APR_ALIGN_DEFAULT(sizeof(thread_node_t))
>   #define SIZEOF_SMS_THREADS_T APR_ALIGN_DEFAULT(sizeof(apr_sms_threads_t))
>   #define SIZEOF_SMS_ACCT_T    APR_ALIGN_DEFAULT(sizeof(apr_sms_threads_acct_t))
>   
>   #define BLOCK_T(mem)         ((block_t *)(mem))
>   #define NODE_T(mem)          ((node_t *)(mem))
>   #define THREAD_NODE_T(mem)   ((thread_node_t *)(mem))
>   #define SMS_THREADS_T(sms)   ((apr_sms_threads_t *)(sms))
>   #define SMS_ACCT_T(sms)      ((apr_sms_threads_acct_t *)(sms))
>   
>   /* Magic numbers :) */
>   
>   #define MIN_ALLOC       0x2000
>   #define MIN_FREE        0x1000
>   #define MAX_FREE        0x100000
>   #define THREAD_MAX_FREE 0x80000
>   
>   static void *apr_sms_threads_malloc(apr_sms_t *sms,
>                                       apr_size_t size)
>   {
>       thread_node_t *thread_node;
>       apr_os_thread_t thread;
>       node_t *node, *sentinel;
>       apr_size_t node_size;
>       apr_uint16_t hash;
>       void *mem;
>   
>       /* Round up the size to the next 8 byte boundary */
>       size = APR_ALIGN_DEFAULT(size) + SIZEOF_BLOCK_T;
>   
>       thread = apr_os_thread_current();
>       hash = thread % HASH_SIZE;
>      
>       /* If the thread wasn't registered before, we will segfault */ 
>       thread_node = SMS_THREADS_T(sms)->hashtable[hash];
>       while (thread_node->thread != thread)
>           thread_node = thread_node->next;
>      
>       node = thread_node->used_sentinel.prev;
>   
>       if (node->avail_size >= size) {
>           mem = node->first_avail;
>           node->avail_size -= size;
>           node->first_avail += size;
>           node->count++;
>   
>           BLOCK_T(mem)->node = node;
>           mem = (char *)mem + SIZEOF_BLOCK_T;
>   
>           return mem;
>       }
>   
>       /* reset the 'last' block, it will be replaced soon */
>       node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>   
>       /* browse the free list for a useable block */
>       sentinel = &thread_node->free_sentinel;
>       sentinel->avail_size = size;
>   
>       node = sentinel->next;
>       while (node->avail_size < size)
>           node = node->next;
>   
>       if (node != sentinel) {
>           node->prev->next = node->next;
>           node->next->prev = node->prev;
>   
>           sentinel = &thread_node->used_sentinel;
>           node->prev = sentinel->prev;
>           node->prev->next = node;
>           node->next = sentinel;
>           sentinel->prev = node;
>           
>           if (node != thread_node->self)
>               thread_node->max_free += node->avail_size;
>           
>           mem = node->first_avail;
>           node->avail_size  -= size;
>           node->first_avail += size;
>           node->count = 1;
>   
>           BLOCK_T(mem)->node = node;
>           mem = (char *)mem + SIZEOF_BLOCK_T;
>   
>           return mem;
>       }
>       
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_acquire(SMS_THREADS_T(sms)->lock);
>       
>       /* browse the shared free list for a useable block */
>       sentinel = &SMS_THREADS_T(sms)->free_sentinel;
>       sentinel->avail_size = size;
>   
>       node = sentinel->next;
>       while (node->avail_size < size)
>           node = node->next;
>   
>       if (node != sentinel) {
>           node->prev->next = node->next;
>           node->next->prev = node->prev;
>   
>           if (SMS_THREADS_T(sms)->lock)
>               apr_lock_release(SMS_THREADS_T(sms)->lock);
>   
>           sentinel = &thread_node->used_sentinel;
>           node->prev = sentinel->prev;
>           node->prev->next = node;
>           node->next = sentinel;
>           sentinel->prev = node;
>          
>           SMS_THREADS_T(sms)->max_free += node->avail_size;
>           
>           mem = node->first_avail;
>           node->avail_size  -= size;
>           node->first_avail += size;
>           node->count = 1;
>   
>           BLOCK_T(mem)->node = node;
>           mem = (char *)mem + SIZEOF_BLOCK_T;
>   
>           return mem;
>       }
>       
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_release(SMS_THREADS_T(sms)->lock);
>       
>       /* we have to allocate a new block from our parent */
>       node_size = size + SMS_THREADS_T(sms)->min_free;
>       if (node_size < SMS_THREADS_T(sms)->min_alloc)
>           node_size = SMS_THREADS_T(sms)->min_alloc;
>       
>       node = apr_sms_malloc(sms->parent, node_size);
>       if (!node) {
>           /* restore the 'last' node to prevent the
>            * next allocation from segfaulting
>            */
>           node = thread_node->used_sentinel.prev;
>           node->first_avail += node->avail_size;
>           node->avail_size = 0;
>           
>           return NULL;
>       }
>   
>       sentinel = &thread_node->used_sentinel;
>       node->prev = sentinel->prev;
>       node->prev->next = node;
>       node->next = sentinel;
>       sentinel->prev = node;
>       
>       mem = node->first_avail = (char *)node + SIZEOF_NODE_T;
>       node->first_avail += size;
>       node->avail_size = node_size - (node->first_avail - (char *)node);
>       node->count = 1;
>   
>       BLOCK_T(mem)->node = node;
>       mem = (char *)mem + SIZEOF_BLOCK_T;
>       
>       return mem;
>   }
>   
>   static apr_status_t apr_sms_threads_free(apr_sms_t *sms, void *mem)
>   {
>       node_t *node, *sentinel;
>       thread_node_t *thread_node;
>       apr_os_thread_t thread;
>       apr_uint16_t hash;
>   
>       node = BLOCK_T((char *)mem - SIZEOF_BLOCK_T)->node;
>   
>       node->count--;
>       if (node->count)
>           return APR_SUCCESS;
>   
>       thread = apr_os_thread_current();
>       hash = thread % HASH_SIZE;
>       
>       thread_node = SMS_THREADS_T(sms)->hashtable[hash];
>       while (thread_node->thread != thread)
>           thread_node = thread_node->next;
>   
>       node->avail_size += node->first_avail - 
>                           ((char *)node + SIZEOF_NODE_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>      
>       if (thread_node->used_sentinel.prev == node)
>           return APR_SUCCESS;
>   
>       node->next->prev = node->prev;
>       node->prev->next = node->next;
>   
>       if (thread_node->max_free >= node->avail_size ||
>           node == thread_node->self) {
>           sentinel = &thread_node->free_sentinel; 
>           node->prev = sentinel->prev;
>           node->prev->next = node;
>           node->next = sentinel;
>           sentinel->prev = node;
>   
>           if (node != thread_node->self)
>               thread_node->max_free -= node->avail_size;
>           
>           return APR_SUCCESS;
>       }
>   
>       if (sms->parent->free_fn && 
>           node->avail_size > SMS_THREADS_T(sms)->max_free)
>           return apr_sms_free(sms->parent, node);
>   
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_acquire(SMS_THREADS_T(sms)->lock);
>       
>       sentinel = &SMS_THREADS_T(sms)->free_sentinel;
>       node->prev = sentinel->prev;
>       node->prev->next = node;
>       node->next = sentinel;
>       sentinel->prev = node;
>   
>       SMS_THREADS_T(sms)->max_free -= node->avail_size;
>      
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_release(SMS_THREADS_T(sms)->lock);
>       
>       return APR_SUCCESS;
>   }
>   
>   static apr_status_t apr_sms_threads_thread_register(apr_sms_t *sms,
>                                                       apr_os_thread_t thread);
>   
>   static apr_status_t apr_sms_threads_reset(apr_sms_t *sms)
>   {
>       node_t *node, *prev, *used_sentinel, *free_sentinel, *free_list;
>       thread_node_t *thread_node;
>       apr_uint16_t hash;
>       apr_size_t min_alloc, max_free;
>   
>       used_sentinel = &SMS_THREADS_T(sms)->used_sentinel;
>       free_sentinel = &SMS_THREADS_T(sms)->free_sentinel;
>   
>       free_list = NULL;
>       
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_acquire(SMS_THREADS_T(sms)->lock);
>       
>       /* Actually, the thread creation function should have installed
>        * a cleanup which effectively kills all the threads created using
>        * this sms. Therefor it is not wise to call reset from another
>        * thread than the 'master' thread.
>        */
>       for (hash = 0; hash < HASH_SIZE; hash++) {
>           while ((thread_node = SMS_THREADS_T(sms)->hashtable[hash]) != NULL) {
>               if ((*thread_node->ref = thread_node->next) != NULL)
>                   thread_node->next->ref = thread_node->ref;
>   
>               node = thread_node->self;
>               node->prev->next = node->next;
>               node->next->prev = node->prev;
>               node->avail_size += node->first_avail - 
>                                   ((char *)node + SIZEOF_NODE_T);
>   
>               node = thread_node->used_sentinel.prev;
>               node->avail_size += node->first_avail -
>                                   ((char *)node + SIZEOF_NODE_T);
>               node->first_avail = (char *)node + SIZEOF_NODE_T;
>               node->next = thread_node->free_sentinel.next;
>               node->next->prev = node;
>   
>               node = thread_node->free_sentinel.prev;
>               node->next = used_sentinel->next;
>               node->next->prev = node;
>   
>               used_sentinel->next = thread_node->used_sentinel.next;
>               used_sentinel->next->prev = used_sentinel;
>   
>               node = NODE_T(thread_node);
>               node->avail_size = thread_node->self->avail_size + 
>                                  SIZEOF_THREAD_NODE_T;
>               node->first_avail = (char *)node + SIZEOF_NODE_T;
>   
>               node->next = used_sentinel->next;
>               node->next->prev = node;
>               used_sentinel->next = node;
>               node->prev = used_sentinel;
>           }
>       }
>       
>       node = SMS_THREADS_T(sms)->self;
>       node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>       node->count = 0;
>       node->prev->next = node->next;
>       node->next->prev = node->prev;
>       
>       node = used_sentinel->prev;
>       node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>   
>       if (sms->parent->free_fn) {
>           min_alloc = SMS_THREADS_T(sms)->min_alloc;
>           max_free  = SMS_THREADS_T(sms)->max_free;
>           used_sentinel->avail_size = min_alloc;
>           while (max_free > min_alloc) {
>               if (node->avail_size <= max_free) {
>                   if (node == used_sentinel)
>                       break;
>   
>                   max_free -= node->avail_size;
>                   node->prev->next = node->next;
>                   node->next->prev = node->prev;
>                   
>                   prev = node->prev;
>                   
>                   node->next = free_sentinel->next;
>                   free_sentinel->next = node;
>                   node->next->prev = node;
>                   node->prev = free_sentinel;
>                   
>                   node = prev;
>               }
>               else
>                   node = node->prev;
>           }
>           SMS_THREADS_T(sms)->max_free = max_free;
>   
>           used_sentinel->prev->next = NULL;
>           free_list = used_sentinel->next;
>       }
>       else {
>           node = used_sentinel->prev;
>           node->next = free_sentinel->next;
>           node->next->prev = node;
>   
>           node = used_sentinel->next;
>           node->prev = free_sentinel;
>           free_sentinel->next = node;
>       }
>       
>       node = SMS_THREADS_T(sms)->self;
>       node->next = node->prev = used_sentinel;
>       used_sentinel->next = used_sentinel->prev = node;
>   
>       sms->accounting = (apr_sms_t *)((char *)sms + SIZEOF_SMS_THREADS_T);
>       
>       apr_sms_threads_thread_register(sms, apr_os_thread_current());
>   
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_release(SMS_THREADS_T(sms)->lock);
>   
>       while ((node = free_list) != NULL) {
>           free_list = node->next;
>           apr_sms_free(sms->parent, node);
>       }
>       
>       return APR_SUCCESS;    
>   }
>   
>   static apr_status_t apr_sms_threads_pre_destroy(apr_sms_t *sms)
>   {
>       /* This function WILL always be called.  However, be aware that the
>        * main sms destroy function knows that it's not wise to try and destroy
>        * the same piece of memory twice, so the destroy function in a child won't
>        * neccesarily be called.  To guarantee we destroy the lock it's therefore
>        * destroyed here.
>        */
>           
>       if (SMS_THREADS_T(sms)->lock) {
>           apr_lock_acquire(SMS_THREADS_T(sms)->lock);
>           apr_lock_destroy(SMS_THREADS_T(sms)->lock);
>           SMS_THREADS_T(sms)->lock = NULL;
>       }
>       
>       return APR_SUCCESS;    
>   }
>   
>   static apr_status_t apr_sms_threads_destroy(apr_sms_t *sms)
>   {
>       node_t *node, *next, *used_sentinel, *free_sentinel;
>       thread_node_t *thread_node;
>       apr_uint16_t hash;
>   
>       for (hash = 0; hash < HASH_SIZE; hash++) {
>           while ((thread_node = SMS_THREADS_T(sms)->hashtable[hash]) != NULL) {
>               if ((*thread_node->ref = thread_node->next) != NULL)
>                   thread_node->next->ref = thread_node->ref;
>   
>               node = thread_node->self;
>               node->prev->next = node->next;
>               node->next->prev = node->prev;
>   
>               used_sentinel = &thread_node->used_sentinel;
>               free_sentinel = &thread_node->free_sentinel;
>               
>               free_sentinel->prev->next = NULL;
>               used_sentinel->prev->next = free_sentinel->next;
>   
>               node = used_sentinel->next;
>               while (node) {
>                   next = node->next;
>                   apr_sms_free(sms->parent, node);
>                   node = next;
>               }
>   
>               apr_sms_free(sms->parent, thread_node);
>           }
>       }
>   
>       node = SMS_THREADS_T(sms)->self;
>       node->prev->next = node->next;
>       node->next->prev = node->prev;
>       
>       used_sentinel = &SMS_THREADS_T(sms)->used_sentinel;
>       free_sentinel = &SMS_THREADS_T(sms)->free_sentinel;
>   
>       free_sentinel->prev->next = NULL;
>       used_sentinel->prev->next = free_sentinel->next;
>   
>       node = used_sentinel->next;
>       while (node) {
>           next = node->next;
>           apr_sms_free(sms->parent, node);
>           node = next;
>       }
>   
>       return apr_sms_free(sms->parent, sms);
>   }
>   
>   static apr_status_t apr_sms_threads_thread_register(apr_sms_t *sms,
>                                                       apr_os_thread_t thread)
>   {
>       thread_node_t *thread_node;
>       node_t *node, *sentinel;
>       apr_uint16_t hash;
>   
>       hash = thread % HASH_SIZE;
>       
>       if (sms->threads > 1 && !SMS_THREADS_T(sms)->lock) {
>           apr_lock_create(&SMS_THREADS_T(sms)->lock,
>                           APR_MUTEX, APR_LOCKALL,
>                           NULL, sms->pool);
>       }
>       
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_acquire(SMS_THREADS_T(sms)->lock);
>   
>       sentinel = &SMS_THREADS_T(sms)->free_sentinel;
>       if ((node = sentinel->next) != sentinel) {
>           node->prev->next = node->next;
>           node->next->prev = node->prev;
>   
>           thread_node = THREAD_NODE_T(node);
>           node = NODE_T((char *)thread_node + SIZEOF_THREAD_NODE_T);
>           node->avail_size = NODE_T(thread_node)->avail_size - 
>                              SIZEOF_THREAD_NODE_T;
>           node->first_avail = (char *)node + SIZEOF_NODE_T;
>       }
>       else {
>           thread_node = apr_sms_malloc(sms->parent, SMS_THREADS_T(sms)->min_alloc);
>           if (!thread_node) {
>               if (SMS_THREADS_T(sms)->lock)
>                   apr_lock_release(SMS_THREADS_T(sms)->lock);
>   
>               return APR_ENOMEM;
>           }
>   
>           node = NODE_T((char *)thread_node + SIZEOF_THREAD_NODE_T);
>           node->first_avail = (char *)node + SIZEOF_NODE_T;
>           node->avail_size = SMS_THREADS_T(sms)->min_alloc -
>                              (node->first_avail - (char *)thread_node);
>       }
>       node->count = 0;
>       
>       thread_node->self = node;
>       thread_node->max_free = SMS_THREADS_T(sms)->thread_max_free;
>   
>       sentinel = &thread_node->used_sentinel;
>       node->prev = node->next = sentinel;
>       sentinel->prev = sentinel->next = node;
>   
>       sentinel = &thread_node->free_sentinel;
>       sentinel->prev = sentinel->next = sentinel;
>   
>       thread_node->thread = thread;
>       if ((thread_node->next = SMS_THREADS_T(sms)->hashtable[hash]) != NULL)
>           thread_node->next->ref = &thread_node->next;
>       thread_node->ref = &SMS_THREADS_T(sms)->hashtable[hash];
>       SMS_THREADS_T(sms)->hashtable[hash] = thread_node;
>   
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_release(SMS_THREADS_T(sms)->lock);
>   
>       return APR_SUCCESS;
>   }
>   
>   static apr_status_t apr_sms_threads_thread_unregister(apr_sms_t *sms,
>                                                         apr_os_thread_t thread)
>   {
>       thread_node_t *thread_node;
>       node_t *node, *prev, *free_list, *used_sentinel, *free_sentinel;
>       apr_uint16_t hash;
>       apr_size_t min_alloc, max_free;
>   
>       hash = thread % HASH_SIZE;
>       free_list = NULL;
>       
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_acquire(SMS_THREADS_T(sms)->lock);
>   
>       thread_node = SMS_THREADS_T(sms)->hashtable[hash];
>       while (thread_node->thread != thread)
>           thread_node = thread_node->next;
>   
>       if ((*thread_node->ref = thread_node->next) != NULL)
>           thread_node->next->ref = thread_node->ref;
>   
>       node = thread_node->self;
>       node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
>       node->prev->next = node->next;
>       node->next->prev = node->prev;
>       
>       used_sentinel = &thread_node->used_sentinel;
>       free_sentinel = &SMS_THREADS_T(sms)->free_sentinel;
>       
>       node = used_sentinel->prev;
>       node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>      
>       node->next = thread_node->free_sentinel.next;
>       node->next->prev = node;
>   
>       node = thread_node->free_sentinel.prev;
>       node->next = used_sentinel;
>       used_sentinel->prev = node;
>   
>       min_alloc = SMS_THREADS_T(sms)->min_alloc;
>       max_free  = SMS_THREADS_T(sms)->max_free;
>       used_sentinel->avail_size = min_alloc;
>       while (max_free > min_alloc) {
>           if (node->avail_size <= max_free) {
>               if (node == used_sentinel)
>                   break;
>       
>               max_free -= node->avail_size;
>               node->prev->next = node->next;
>               node->next->prev = node->prev;
>               prev = node->prev;
>   
>               node->next = free_sentinel->next;
>               free_sentinel->next = node;
>               node->next->prev = node;
>               node->prev = free_sentinel;
>               
>               node = prev;
>           }
>           else
>               node = node->prev;
>       }
>                               
>       used_sentinel->prev->next = NULL;
>       free_list = used_sentinel->next;
>                       
>       node = NODE_T(thread_node);
>       node->avail_size = thread_node->self->avail_size + 
>                          SIZEOF_THREAD_NODE_T;
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>   
>       if (max_free >= node->avail_size) {
>           max_free -= node->avail_size;
>   
>           node->next = free_sentinel->next;
>           node->next->prev = node;
>           free_sentinel->next = node;
>           node->prev = free_sentinel;
>       }
>       else {
>           node->next = free_list;
>           free_list = node;
>       }
>       
>       SMS_THREADS_T(sms)->max_free = max_free;
>       
>       if (SMS_THREADS_T(sms)->lock)
>           apr_lock_release(SMS_THREADS_T(sms)->lock);
>   
>       while ((node = free_list) != NULL) {
>           free_list = node->next;
>           apr_sms_free(sms->parent, node);
>       }
>       
>       return APR_SUCCESS;
>   }
>   
>   static void *apr_sms_threads_acct_malloc(apr_sms_t *sms, apr_size_t size)
>   {
>       apr_sms_threads_t *tms;
>       node_t *node, *sentinel;
>       apr_size_t node_size;
>       void *mem;
>   
>       tms = SMS_ACCT_T(sms)->tms;
>   
>       /* Round up the size to the next 8 byte boundary */
>       size = APR_ALIGN_DEFAULT(size) + SIZEOF_BLOCK_T;
>   
>       if (tms->lock)
>           apr_lock_acquire(tms->lock);
>       
>       node = tms->used_sentinel.prev;
>   
>       if (node->avail_size >= size) {
>           mem = node->first_avail;
>           node->avail_size -= size;
>           node->first_avail += size;
>           node->count++;
>   
>           if (tms->lock)
>               apr_lock_release(tms->lock);
>       
>           BLOCK_T(mem)->node = node;
>           mem = (char *)mem + SIZEOF_BLOCK_T;
>   
>           return mem;
>       }
>   
>       /* reset the 'last' block, it will be replaced soon */
>       node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>   
>       /* browse the free list for a useable block */
>       sentinel = &tms->free_sentinel;
>       sentinel->avail_size = size;
>   
>       node = sentinel->next;
>       while (node->avail_size < size)
>           node = node->next;
>   
>       if (node != sentinel) {
>           node->prev->next = node->next;
>           node->next->prev = node->prev;
>   
>           sentinel = &tms->used_sentinel;
>           node->prev = sentinel->prev;
>           node->prev->next = node;
>           node->next = sentinel;
>           sentinel->prev = node;
>           
>           if (node != tms->self)
>               tms->max_free += node->avail_size;
>   
>           mem = node->first_avail;
>           node->avail_size  -= size;
>           node->first_avail += size;
>           node->count = 1;
>   
>           if (tms->lock)
>               apr_lock_release(tms->lock);
>   
>           BLOCK_T(mem)->node = node;
>           mem = (char *)mem + SIZEOF_BLOCK_T;
>   
>           return mem;
>       }
>       
>       /* we have to allocate a new block from our parent */
>       node_size = size + tms->min_free;
>       if (node_size < tms->min_alloc)
>           node_size = tms->min_alloc;
>       
>       node = apr_sms_malloc(sms->parent, node_size);
>       if (!node) {
>           /* restore the 'last' node, so the next allocation
>            * will not segfault
>            */
>           node = tms->used_sentinel.prev;
>           node->first_avail += node->avail_size;
>           node->avail_size = 0;
>   
>           if (tms->lock)
>               apr_lock_release(tms->lock);
>           
>           return NULL;
>       }
>   
>       sentinel = &tms->used_sentinel;
>       node->prev = sentinel->prev;
>       node->prev->next = node;
>       node->next = sentinel;
>       sentinel->prev = node;
>       
>       mem = node->first_avail = (char *)node + SIZEOF_NODE_T;
>       node->first_avail += size;
>       node->avail_size = node_size - (node->first_avail - (char *)node);
>       node->count = 1;
>   
>       if (tms->lock)
>           apr_lock_release(tms->lock);
>   
>       BLOCK_T(mem)->node = node;
>       mem = (char *)mem + SIZEOF_BLOCK_T;
>       
>       return mem;
>   }
>   
>   static apr_status_t apr_sms_threads_acct_free(apr_sms_t *sms, void *mem)
>   {
>       apr_sms_threads_t *tms;
>       node_t *node, *sentinel;
>   
>       tms = SMS_ACCT_T(sms)->tms;
>       node = BLOCK_T((char *)mem - SIZEOF_BLOCK_T)->node;
>   
>       if (tms->lock)
>           apr_lock_acquire(tms->lock);
>   
>       node->count--;
>   
>       if (node->count) {
>           if (tms->lock)
>               apr_lock_release(tms->lock);
>   
>           return APR_SUCCESS;
>       }
>   
>       node->avail_size += node->first_avail - ((char *)node + SIZEOF_NODE_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>       
>       if (tms->used_sentinel.prev != node) {
>           node->next->prev = node->prev;
>           node->prev->next = node->next;
>   
>           if (sms->parent->free_fn &&
>               node->avail_size > tms->max_free &&
>               node != tms->self) {
>               if (tms->lock)
>                   apr_lock_release(tms->lock);
>   
>               return apr_sms_free(sms->parent, node);
>           }
>           
>           sentinel = &tms->free_sentinel;
>           node->prev = sentinel->prev;
>           node->prev->next = node;
>           node->next = sentinel;
>           sentinel->prev = node;
>   
>           if (node != tms->self)
>               tms->max_free -= node->avail_size;
>       }
>   
>       if (tms->lock)
>           apr_lock_release(tms->lock);
>   
>       return APR_SUCCESS;
>   }
>   
>   APR_DECLARE(apr_status_t) apr_sms_threads_create(apr_sms_t **sms, 
>                                                    apr_sms_t *pms)
>   {
>       return apr_sms_threads_create_ex(sms, pms, MIN_ALLOC, MIN_FREE, MAX_FREE);    
>   }
>   
>   APR_DECLARE(apr_status_t) apr_sms_threads_create_ex(apr_sms_t **sms, 
>                                                       apr_sms_t *pms,
>                                                       apr_size_t min_alloc,
>                                                       apr_size_t min_free,
>                                                       apr_size_t max_free)
>   {
>       apr_sms_t *new_sms, *ams;
>       apr_sms_threads_t *tms;
>       node_t *node;
>       apr_status_t rv;
>   
>       *sms = NULL;
>   
>       min_alloc = APR_ALIGN_DEFAULT(min_alloc);
>       min_free  = APR_ALIGN_DEFAULT(min_free);
>       
>       /* We're not a top level module, ie we have a parent, so
>        * we allocate the memory for the structure from our parent.
>        * This is safe as we shouldn't outlive our parent...
>        */
>       new_sms = apr_sms_calloc(pms, min_alloc);
>       if (!new_sms)
>           return APR_ENOMEM;
>   
>       if ((rv = apr_sms_init(new_sms, pms)) != APR_SUCCESS)
>           return rv;
>   
>       new_sms->malloc_fn            = apr_sms_threads_malloc;
>       new_sms->free_fn              = apr_sms_threads_free;
>       new_sms->reset_fn             = apr_sms_threads_reset;
>       new_sms->pre_destroy_fn       = apr_sms_threads_pre_destroy;
>       new_sms->destroy_fn           = apr_sms_threads_destroy;
>       new_sms->thread_register_fn   = apr_sms_threads_thread_register;
>       new_sms->thread_unregister_fn = apr_sms_threads_thread_unregister;
>       new_sms->identity             = module_identity;
>   
>       ams = (apr_sms_t *)((char *)new_sms + SIZEOF_SMS_THREADS_T);
>       
>       node = NODE_T((char *)ams + SIZEOF_SMS_ACCT_T);
>       node->first_avail = (char *)node + SIZEOF_NODE_T;
>       node->avail_size  = min_alloc - (node->first_avail - (char *)new_sms);
>       node->count       = 0;
>       
>       tms = SMS_THREADS_T(new_sms);
>       tms->min_alloc       = min_alloc;
>       tms->min_free        = min_free;
>       tms->max_free        = max_free;
>       tms->thread_max_free = THREAD_MAX_FREE;
>       tms->self            = node;
>       
>       node->next = node->prev = &tms->used_sentinel;
>       tms->used_sentinel.next = tms->used_sentinel.prev = node;
>       tms->free_sentinel.next = tms->free_sentinel.prev = &tms->free_sentinel;
>   
>       apr_sms_init(ams, new_sms);
>       ams->malloc_fn = apr_sms_threads_acct_malloc;
>       ams->free_fn   = apr_sms_threads_acct_free;
>       ams->identity  = module_acct_identity;
>       SMS_ACCT_T(ams)->tms = tms;
>       apr_sms_post_init(ams);
>       
>       new_sms->accounting = ams;
>       
>       apr_sms_post_init(new_sms);
>       
>       *sms = new_sms;
>       return APR_SUCCESS;
>   }
>   
>   #endif /* APR_HAS_THREADS */
>   
>   
>   
>   
--
Ian Holsman
Performance Measurement & Analysis
CNET Networks    -    415 364-8608