dougm       00/05/23 13:53:38

  Added:       src/modules/perl modperl_tipool.c modperl_tipool.h
  Log:
  "thread item pool" - generic logic broken out from modperl_interp.c so it can be 
reused for other things, e.g. database connection handles
  
  Revision  Changes    Path
  1.1                  modperl-2.0/src/modules/perl/modperl_tipool.c
  
  Index: modperl_tipool.c
  ===================================================================
  #ifdef USE_ITHREADS
  
  #include "mod_perl.h"
  
  /*
   * tipool == "thread item pool"
   * this module is intended to provide generic stuctures/functions
   * for managing a "pool" of a given items (data structures) within a threaded
   * process.  at the moment, mod_perl uses this module to manage a pool
   * of PerlInterpreter objects.  it should be quite easy to reuse for
   * other data, such as database connection handles and the like.
   * while it is "generic" it is also tuned for Apache, making use of
   * ap_pool_t and the like, and implementing start/max/{min,max}_spare/
   * max_requests configuration.
   * this is another "proof-of-concept", plenty of room for improvement here
   */
  
  modperl_list_t *modperl_list_new(ap_pool_t *p)
  {
      modperl_list_t *listp = 
          (modperl_list_t *)ap_pcalloc(p, sizeof(*listp));
      return listp;
  }
  
  modperl_list_t *modperl_list_last(modperl_list_t *list)
  {
      while (list->next) {
          list = list->next;
      }
  
      return list;
  }
  
  modperl_list_t *modperl_list_first(modperl_list_t *list)
  {
      while (list->prev) {
          list = list->prev;
      }
  
      return list;
  }
  
  modperl_list_t *modperl_list_append(modperl_list_t *list,
                                      modperl_list_t *new_list)
  {
      modperl_list_t *last;
  
      new_list->prev = new_list->next = NULL;
  
      if (!list) {
          return new_list;
      }
  
      last = modperl_list_last(list);
  
      last->next = new_list;
      new_list->prev = last;
  
      return list;
  }
  
  modperl_list_t *modperl_list_prepend(modperl_list_t *list,
                                       modperl_list_t *new_list)
  {
      new_list->prev = new_list->next = NULL;
  
      if (!list) {
          return new_list;
      }
  
      if (list->prev) {
          list->prev->next = new_list;
          new_list->prev = list->prev;
      }
  
      list->prev = new_list;
      new_list->next = list;
  
      return new_list;
  }
  
  modperl_list_t *modperl_list_remove(modperl_list_t *list,
                                      modperl_list_t *rlist)
  {
      modperl_list_t *tmp = list;
    
      while (tmp) {
          if (tmp != rlist) {
              tmp = tmp->next;
          }
          else {
              if (tmp->prev) {
                  tmp->prev->next = tmp->next;
              }
              if (tmp->next) {
                  tmp->next->prev = tmp->prev;
              }
              if (list == tmp) {
                  list = list->next;
              }
  
              break;
        }
      }
  
  #ifdef MP_TRACE
      if (!tmp) {
          /* should never happen */
          MP_TRACE_i(MP_FUNC, "failed to find 0x%lx in list 0x%lx\n",
                     (unsigned long)rlist, (unsigned long)list);
      }
  #endif
  
      return list;
  }
  
  modperl_list_t *modperl_list_remove_data(modperl_list_t *list,
                                           void *data,
                                           modperl_list_t **listp)
  {
      modperl_list_t *tmp = list;
    
      while (tmp) {
          if (tmp->data != data) {
              tmp = tmp->next;
          }
          else {
              *listp = tmp;
              if (tmp->prev) {
                  tmp->prev->next = tmp->next;
              }
              if (tmp->next) {
                  tmp->next->prev = tmp->prev;
              }
              if (list == tmp) {
                  list = list->next;
              }
  
              break;
        }
      }
  
      return list;
  }
  
  modperl_tipool_t *modperl_tipool_new(ap_pool_t *p,
                                       modperl_tipool_config_t *cfg,
                                       modperl_tipool_vtbl_t *func,
                                       void *data)
  {
      modperl_tipool_t *tipool =
          (modperl_tipool_t *)ap_pcalloc(p, sizeof(*tipool));
  
      tipool->ap_pool = p;
      tipool->cfg = cfg;
      tipool->func = func;
      tipool->data = data;
  
      MUTEX_INIT(&tipool->tiplock);
      COND_INIT(&tipool->available);
  
      return tipool;
  }
  
  void modperl_tipool_init(modperl_tipool_t *tipool)
  {
      int i;
  
      for (i=0; i<tipool->cfg->start; i++) {
          void *item =
              (*tipool->func->tipool_sgrow)(tipool, tipool->data);
  
          modperl_tipool_add(tipool, item);
      }
  
      MP_TRACE_i(MP_FUNC, "start=%d, max=%d, min_spare=%d, max_spare=%d\n",
                 tipool->cfg->start, tipool->cfg->max,
                 tipool->cfg->min_spare, tipool->cfg->max_spare);
  
  }
  
  void modperl_tipool_destroy(modperl_tipool_t *tipool)
  {
      while (tipool->idle) {
          if (tipool->func->tipool_destroy) {
              (*tipool->func->tipool_destroy)(tipool, tipool->data,
                                              tipool->idle->data);
          }
          tipool->size--;
          tipool->idle = tipool->idle->next;
      }
  
      if (tipool->busy) {
          MP_TRACE_i(MP_FUNC, "ERROR: %d items still in use\n",
                     tipool->in_use);
      }
  
      MUTEX_DESTROY(&tipool->tiplock);
      COND_DESTROY(&tipool->available);
  }
  
  void modperl_tipool_add(modperl_tipool_t *tipool, void *data)
  {
      modperl_list_t *listp = modperl_list_new(tipool->ap_pool);
  
      listp->data = data;
  
      /* assuming tipool->tiplock has already been aquired */
  
      tipool->idle = modperl_list_append(tipool->idle, listp);
  
      tipool->size++;
  
      MP_TRACE_i(MP_FUNC, "added 0x%lx (size=%d)\n",
                 (unsigned long)listp, tipool->size);
  }
  
  void modperl_tipool_remove(modperl_tipool_t *tipool, modperl_list_t *listp)
  {
      /* assuming tipool->tiplock has already been aquired */
  
      tipool->idle = modperl_list_remove(tipool->idle, listp);
  
      tipool->size--;
      MP_TRACE_i(MP_FUNC, "removed 0x%lx (size=%d)\n",
                 (unsigned long)listp, tipool->size);
  }
  
  modperl_list_t *modperl_tipool_pop(modperl_tipool_t *tipool)
  {
      modperl_list_t *head;
  
      modperl_tipool_lock(tipool);
  
      if (tipool->size == tipool->in_use) {
          if (tipool->size < tipool->cfg->max) {
              MP_TRACE_i(MP_FUNC,
                         "no idle items, size %d < %d max\n",
                         tipool->size, tipool->cfg->max);
              if (tipool->func->tipool_rgrow) {
                  void * item =
                      (*tipool->func->tipool_sgrow)(tipool, tipool->data);
  
                  modperl_tipool_add(tipool, item);
              }
          }
          /* block until an item becomes available */
          modperl_tipool_wait(tipool);
      }
  
      head = tipool->idle;
  
      tipool->idle = modperl_list_remove(tipool->idle, head);
      tipool->busy = modperl_list_append(tipool->busy, head);
  
      tipool->in_use++;
  
      /* XXX: this should never happen */
      if (!head) {
          MP_TRACE_i(MP_FUNC, "PANIC: no items available, %d of %d in use\n", 
                     tipool->in_use, tipool->size);
          abort();
      }
  
      modperl_tipool_unlock(tipool);
  
      return head;
  }
  
  static void modperl_tipool_putback_base(modperl_tipool_t *tipool,
                                          modperl_list_t *listp,
                                          void *data,
                                          int num_requests)
  {
      int max_spare, max_requests;
  
      modperl_tipool_lock(tipool);
  
      /* remove from busy list, add back to idle */
      /* XXX: option to sort list, e.g. on num_requests */
  
      if (listp) {
          tipool->busy = modperl_list_remove(tipool->busy, listp);
      }
      else {
          tipool->busy = modperl_list_remove_data(tipool->busy, data, &listp);
      }
  
      tipool->idle = modperl_list_prepend(tipool->idle, listp);
  
      tipool->in_use--;
  
  #ifdef MP_TRACE
      if (!tipool->busy && tipool->func->tipool_dump) {
          MP_TRACE_i(MP_FUNC, "all items idle:\n");
          MP_TRACE_i_do((*tipool->func->tipool_dump)(tipool,
                                                     tipool->data,
                                                     tipool->idle));
      }
  #endif
  
      MP_TRACE_i(MP_FUNC, "0x%lx now available (%d in use, %d running)\n",
                 (unsigned long)listp->data, tipool->in_use, tipool->size);
  
      if (tipool->in_use == (tipool->cfg->max - 1)) {
          /* hurry up, another thread may be blocking */
          modperl_tipool_broadcast(tipool);
          modperl_tipool_unlock(tipool);
          return;
      }
  
      max_spare = (tipool->size > tipool->cfg->max_spare);
      max_requests = ((num_requests > 0) &&
                      (num_requests > tipool->cfg->max_requests));
  
      if (max_spare) {
          MP_TRACE_i(MP_FUNC, "throttle down (max_spare=%d, %d running)\n",
                     tipool->cfg->max_spare, tipool->size);
      }
      else if (max_requests) {
          MP_TRACE_i(MP_FUNC, "max requests %d reached\n",
                     tipool->cfg->max_requests);
      }
  
      /* XXX: this management should probably be happening elsewhere
       * like in a thread spawned at startup
       */
      if (max_spare || max_requests) {
          modperl_tipool_remove(tipool, listp);
  
          if (tipool->func->tipool_destroy) {
              (*tipool->func->tipool_destroy)(tipool, tipool->data,
                                              listp->data);
          }
  
          if (max_requests && (tipool->size < tipool->cfg->min_spare)) {
              if (tipool->func->tipool_rgrow) {
                  void *item =
                      (*tipool->func->tipool_sgrow)(tipool,
                                                    tipool->data);
  
                  MP_TRACE_i(MP_FUNC, "new item: size %d < %d min_spare\n",
                             tipool->size, tipool->cfg->min_spare);
  
                  modperl_tipool_add(tipool, item);
              }
          }
      }
  
      modperl_tipool_unlock(tipool);
  }
  
  /* _data functions are so structures (e.g. modperl_interp_t) don't
   * need to maintain a pointer back to the modperl_list_t
   */
  
  void modperl_tipool_putback_data(modperl_tipool_t *tipool,
                                   void *data,
                                   int num_requests)
  {
      modperl_tipool_putback_base(tipool, NULL, data, num_requests);
  }
  
  void modperl_tipool_putback(modperl_tipool_t *tipool,
                              modperl_list_t *listp,
                              int num_requests)
  {
      modperl_tipool_putback_base(tipool, listp, NULL, num_requests);
  }
  
  #endif /* USE_ITHREADS */
  
  
  
  1.1                  modperl-2.0/src/modules/perl/modperl_tipool.h
  
  Index: modperl_tipool.h
  ===================================================================
  #ifndef MODPERL_TIPOOL_H
  
  #ifdef USE_ITHREADS
  
  modperl_list_t *modperl_list_new(ap_pool_t *p);
  
  modperl_list_t *modperl_list_last(modperl_list_t *list);
  
  modperl_list_t *modperl_list_first(modperl_list_t *list);
  
  modperl_list_t *modperl_list_append(modperl_list_t *list,
                                      modperl_list_t *new_list);
  
  modperl_list_t *modperl_list_prepend(modperl_list_t *list,
                                       modperl_list_t *new_list);
  
  modperl_list_t *modperl_list_remove(modperl_list_t *list,
                                      modperl_list_t *rlist);
  
  modperl_tipool_t *modperl_tipool_new(ap_pool_t *p,
                                       modperl_tipool_config_t *cfg,
                                       modperl_tipool_vtbl_t *func,
                                       void *data);
  
  void modperl_tipool_init(modperl_tipool_t *tipool);
  
  void modperl_tipool_destroy(modperl_tipool_t *tipool);
  
  void modperl_tipool_add(modperl_tipool_t *tipool, void *data);
  
  void modperl_tipool_remove(modperl_tipool_t *tipool, modperl_list_t *listp);
  
  modperl_list_t *modperl_tipool_pop(modperl_tipool_t *tipool);
  
  void modperl_tipool_putback(modperl_tipool_t *tipool,
                              modperl_list_t *listp,
                              int num_requests);
  
  void modperl_tipool_putback_data(modperl_tipool_t *tipool, void *data,
                                   int num_requests);
  
  #define modperl_tipool_wait(tipool) \
      while (tipool->size == tipool->in_use) { \
          MP_TRACE_i(MP_FUNC, "waiting for available tipool item\n"); \
          MP_TRACE_i(MP_FUNC, "(%d items in use, %d alive)\n", \
                     tipool->in_use, tipool->size); \
          COND_WAIT(&tipool->available, &tipool->tiplock); \
      }
  
  #define modperl_tipool_broadcast(tipool) \
      MP_TRACE_i(MP_FUNC, "broadcast available tipool item\n"); \
      COND_SIGNAL(&tipool->available)
  
  #define modperl_tipool_lock(tipool) \
      MP_TRACE_i(MP_FUNC, "about to lock tipool\n"); \
      MUTEX_LOCK(&tipool->tiplock); \
      MP_TRACE_i(MP_FUNC, "aquired tipool lock\n")
  
  #define modperl_tipool_unlock(tipool) \
      MP_TRACE_i(MP_FUNC, "about to unlock tipool\n"); \
      MUTEX_UNLOCK(&tipool->tiplock); \
      MP_TRACE_i(MP_FUNC, "released tipool lock\n")
  
  #endif /* USE_ITHREADS */
  
  #endif /* MODPERL_TIPOOL_H */
  
  
  

Reply via email to