*** stunnel-3.20.orig/sthreads.c	Sun Aug 12 11:14:51 2001
--- stunnel-3.20/sthreads.c	Tue Oct 23 11:41:42 2001
***************
*** 28,37 ****
  
  #ifdef USE_PTHREAD
  
  #include <pthread.h>
  
! pthread_mutex_t stunnel_cs[CRIT_SECTIONS];
  
  pthread_mutex_t lock_cs[CRYPTO_NUM_LOCKS];
  pthread_attr_t pth_attr;
  
--- 28,95 ----
  
  #ifdef USE_PTHREAD
  
+ 
+ /**
+  * thread pooliging for posix pthreads.
+  */
+ 
+ 
+ extern server_options options;
+ 
  #include <pthread.h>
  
! #ifndef MAX_CLIENTS
! #ifdef FD_SETSIZE
! #define MAX_CLIENTS    ((FD_SETSIZE-24)/2)
! #else
! #define MAX_CLIENTS    500
! #endif
! #endif
! 
! #define THDATA_MAGIC 3849086
! 
! #define TH_NONE   0
! #define TH_IDLE   1
! #define TH_ACTIVE 2
! #define TH_START  3
! #define TH_STOP   4
! 
! 
! /*
!  * In this thread pool threads are divided to tree groups (LOW,NORM,HIGH)
!  * if there is nothing to do for thread then threads quits.
!  * HIGH threads are killed first NORM then and LOW last.
!  * Each group has it's own IDLE_TIMEOUT
!  * (this is quite ad hoc solution)
!  */
! 
! #define LOW_THREADS   10     
! #define HIGH_THREADS  100
! 
! 
! #define IDLE_TIMEOUT_LOW   300  
! #define IDLE_TIMEOUT_NORM  60
! #define IDLE_TIMEOUT_HIGH  10    
! 
! 
! typedef struct {
!     int            status;        /* status  TH_NONE,TH_IDLE,TH_ACTIVE,TH_START, TH_STOP */
!     int            magic;         /* magic number for checking pointers */
!     int            index;         /* index of this in table  */
!     pthread_t      thread;        /* actual posix thread */
!     pthread_cond_t  cond;         /* condition used to wake up thread */
!     void           *data;         /* data to give to thread */
!     void          (*proc)(int);   /* actual thread job function */
!     long long       start_time;   /* when started */
!     int             count;        /* how many times used */
! } THDATA;
! 
! static int thread_count = 0;  
! static THDATA *thread_pool[MAX_CLIENTS];
  
+ pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER;
+ 
+ pthread_mutex_t stunnel_cs[CRIT_SECTIONS];
  pthread_mutex_t lock_cs[CRYPTO_NUM_LOCKS];
  pthread_attr_t pth_attr;
  
***************
*** 79,87 ****
      return (unsigned long)pthread_self();
  }
  
! int create_client(int ls, int s, void (*cli)(int)) {
!      pthread_t thread;
       sigset_t mask, oldmask;
  
       sigemptyset(&mask);
       sigaddset(&mask, SIGCHLD);
--- 137,279 ----
      return (unsigned long)pthread_self();
  }
  
! 
! static void *th_child(void *data)
! {
!     THDATA *th = (THDATA *) data;
!     struct timeval now;
!     struct timespec timeout;
!     int my_timeout;
!     int ret;
!     int stop = 0;
!     char *cl;
! 
!     if(th->magic != THDATA_MAGIC) abort();
! 
!     th->status = TH_ACTIVE;
! 
!     if(th->index < LOW_THREADS) {
! 	my_timeout = IDLE_TIMEOUT_LOW;
! 	cl = "LOW";
!     } else if(th->index < HIGH_THREADS) {
! 	my_timeout = IDLE_TIMEOUT_NORM;
! 	cl = "NORM";
!     } else {
! 	my_timeout = IDLE_TIMEOUT_HIGH;
! 	cl = "HIGH";
!     }
! 
!     do {
! 
! 	th->count++;
! 
! 	(th->proc)((int) th->data);
! 
! 	th->status = TH_IDLE;
! 
! 	while(!stop && (th->status != TH_ACTIVE)) {
! 
! 	    gettimeofday(&now,NULL);
! 
! 	    timeout.tv_sec  = now.tv_sec + my_timeout;
! 	    timeout.tv_nsec = now.tv_usec * 1000;
! 	    
! 	    // wait for next connection.
! 	    
! 	    pthread_mutex_lock(&pool_mutex);
! 
! 	    if(my_timeout > 0) {
! 		ret = 0;
! 		ret = pthread_cond_timedwait(&th->cond,&pool_mutex,&timeout);
! 		
! 		if(ret == ETIMEDOUT) {
! 		    thread_pool[th->index] = NULL;
! 		    th->status = TH_STOP;
! 		    thread_count--;
! 		    stop = 1;
! 		}
! 	    } else {
! 		pthread_cond_wait(&th->cond,&pool_mutex);
! 	    }
! 
! 	    pthread_mutex_unlock(&pool_mutex);
! 	}
! 
! 
!     } while(!stop);
! 
!     log(LOG_WARNING, "%s thread(%d) idle timout; active(%d) class(%s)", options.servname, th->index, thread_count, cl);
! 
!     pthread_cond_destroy(&th->cond);
!     th->magic = 0;
!     free(th);
! 
!     return NULL;
! }
! 
! static THDATA th_reserve = { -1, THDATA_MAGIC };
! 
! 
! int create_client(int ls, int s, void (*cli)(int)) 
! {
!     // pthread_t thread;
       sigset_t mask, oldmask;
+      int i;
+      int mark = -1;
+      THDATA *found = NULL;
+      THDATA *th;
+ 
+      pthread_mutex_lock(&pool_mutex);
+      for(i=0; i < MAX_CLIENTS; i++) {
+ 	 THDATA *t = thread_pool[i];
+ 	 if(t == NULL) {
+ 	     if(mark < 0) mark = i;
+ 	 } else if(t == &th_reserve) {
+ 	     // well it's starting
+ 	 } else if(t->status == TH_IDLE) {
+ 	     found = t;
+ 	     break;
+ 	 }
+      }
+ 
+      if(found) {
+ 	 found->status = TH_ACTIVE;
+ 	 found->data = (void *) s;
+ 	 found->proc = cli;
+ 	 pthread_cond_signal(&found->cond);
+      } else if(mark >= 0) {
+ 	 thread_pool[mark] = &th_reserve;
+      }
+ 
+      pthread_mutex_unlock(&pool_mutex);
+ 
+      if(found) {
+ 	 log(LOG_NOTICE, "%s using old thread(%d) count(%d)", options.servname, found->index, found->count);
+ 	 return 0;
+      }
+ 
+      if(mark < 0) {
+ 	 log(LOG_WARNING, "%s TOO MANY CLIENTS (%d)", options.servname, MAX_CLIENTS);
+ 	 return -1;
+      }
+ 
+      // Create new thread
+ 
+      th = calloc(sizeof(THDATA),1);
+ 
+      th->magic  = THDATA_MAGIC;
+      th->status = TH_START;
+      th->index  = mark;
+      pthread_cond_init(&th->cond, NULL);
+ 
+      pthread_mutex_lock(&pool_mutex);
+      thread_pool[mark] = th;
+      pthread_mutex_unlock(&pool_mutex);
+ 
+      log(LOG_NOTICE, "%s start new thread (%d)", options.servname, th->index);
+ 
+      th->data = (void *) s;
+      th->proc = cli;
  
       sigemptyset(&mask);
       sigaddset(&mask, SIGCHLD);
***************
*** 90,102 ****
       sigaddset(&mask, SIGINT);
       sigaddset(&mask, SIGHUP);
       pthread_sigmask(SIG_BLOCK, &mask, &oldmask); /* block SIGCHLD */
!      if(pthread_create(&thread, &pth_attr, (void *)cli, (void *)s)) {
           /* SIGCHLD will remain blocked here */
           closesocket(s);
           return -1;
       }
       pthread_sigmask(SIG_SETMASK, &oldmask, NULL); /* restore the mask */
       return 0;
  }
  
  #endif
--- 282,304 ----
       sigaddset(&mask, SIGINT);
       sigaddset(&mask, SIGHUP);
       pthread_sigmask(SIG_BLOCK, &mask, &oldmask); /* block SIGCHLD */
! 
!      if(pthread_create(&th->thread, &pth_attr, (void *)th_child, (void *)th)) {
           /* SIGCHLD will remain blocked here */
+ 	 th->status = TH_NONE;
           closesocket(s);
           return -1;
       }
+ 
+      pthread_mutex_lock(&pool_mutex);
+      thread_count++;
+      pthread_mutex_unlock(&pool_mutex);
+ 
       pthread_sigmask(SIG_SETMASK, &oldmask, NULL); /* restore the mask */
+ 
+ 
       return 0;
+ 
  }
  
  #endif
