apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stri...@apache.org
Subject cvs commit: apr/memory/unix apr_sms_threads.c Makefile.in
Date Thu, 12 Jul 2001 16:50:23 GMT
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 */
  
  
  
  

Mime
View raw message