From 322a53eb563e53ab142510af0e0249961667fe7f Mon Sep 17 00:00:00 2001 From: Robert James Kaes Date: Tue, 12 Sep 2000 00:07:44 +0000 Subject: tinyproxy now uses a pool of threads to handle connections. All the work for creating new threads, deleting old thread, and generally managing the pool is done here. --- src/thread.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/thread.h | 36 ++++++++++ 2 files changed, 250 insertions(+) create mode 100644 src/thread.c create mode 100644 src/thread.h diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000..6e5d0fb --- /dev/null +++ b/src/thread.c @@ -0,0 +1,214 @@ +/* $Id: thread.c,v 1.1 2000-09-12 00:07:44 rjkaes Exp $ + * + * Handles the creation/destruction of the various threads required for + * processing incoming connections. + * + * Copyright (C) 2000 Robert James Kaes (rjkaes@flarenet.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include "tinyproxy.h" + +#include "log.h" +#include "reqs.h" +#include "sock.h" +#include "thread.h" + +static int listenfd; +static socklen_t addrlen; +struct thread_s { + pthread_t tid; + enum { T_EMPTY, T_WAITING, T_CONNECTED } status; +}; +static struct thread_s *thread_ptr; +static pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER; + +struct thread_config_s { + unsigned int maxclients, maxrequestsperchild; + unsigned int maxspareservers, minspareservers, startservers; +} thread_config; + +static unsigned int servers_waiting; /* servers waiting for a connection */ +static pthread_mutex_t servers_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t servers_cond = PTHREAD_COND_INITIALIZER; + +#define LOCK_SERVERS() pthread_mutex_lock(&servers_mutex) +#define UNLOCK_SERVERS() pthread_mutex_unlock(&servers_mutex) + +/* + * Set the configuration values for the various thread related settings. + */ +int thread_configure(thread_config_t type, unsigned int val) +{ + switch (type) { + case THREAD_MAXCLIENTS: + thread_config.maxclients = val; + break; + case THREAD_MAXSPARESERVERS: + thread_config.maxspareservers = val; + break; + case THREAD_MINSPARESERVERS: + thread_config.minspareservers = val; + break; + case THREAD_STARTSERVERS: + thread_config.startservers = val; + break; + case THREAD_MAXREQUESTSPERCHILD: + thread_config.maxrequestsperchild = val; + break; + default: + DEBUG2("Invalid type (%d)", type); + return -1; + } + + return 0; +} + +/* + * This is the main (per thread) loop. + */ +static void *thread_main(void *arg) +{ + int connfd; + struct sockaddr *cliaddr; + socklen_t clilen; + struct thread_s *ptr; + + ptr = (struct thread_s *)arg; + + cliaddr = malloc(addrlen); + if (!cliaddr) { + log(LOG_ERR, "Could not allocate memory"); + return NULL; + } + + for ( ; ; ) { + clilen = addrlen; + pthread_mutex_lock(&mlock); + connfd = accept(listenfd, cliaddr, &clilen); + pthread_mutex_unlock(&mlock); + + LOCK_SERVERS(); + ptr->status = T_CONNECTED; + servers_waiting--; + UNLOCK_SERVERS(); + + handle_connection(connfd); + close(connfd); + + LOCK_SERVERS(); + ptr->status = T_WAITING; + servers_waiting++; + UNLOCK_SERVERS(); + } +} +/* + * Create the initial pool of threads. + */ +int thread_pool_create(void) +{ + unsigned int i; + + if (thread_config.maxclients == 0) { + log(LOG_ERR, "You must set MaxClients to a value greater than 0"); + return -1; + } + if (thread_config.startservers == 0) { + log(LOG_ERR, "You must set StartServers to a value greate than 0"); + return -1; + } + + thread_ptr = calloc(thread_config.maxclients, sizeof(struct thread_s)); + if (!thread_ptr) + return -1; + + if (thread_config.startservers > thread_config.maxclients) { + log(LOG_WARNING, "Can not start more than 'MaxClients' servers. Starting %d servers", thread_config.maxclients); + thread_config.startservers = thread_config.maxclients; + } + + for (i = 0; i < thread_config.startservers; i++) { + thread_ptr[i].status = T_WAITING; + servers_waiting++; + pthread_create(&thread_ptr[i].tid, NULL, &thread_main, &thread_ptr[i]); + } + for (i = thread_config.startservers; i < thread_config.maxclients; i++) { + thread_ptr[i].status = T_EMPTY; + } + + return 0; +} + +/* + * Keep the proper number of servers running. This is the birth and death + * of the servers. It monitors this at least once a second. + */ +int thread_main_loop(void) +{ + int i; + struct timeval tv; + struct timespec ts; + + while (config.quit == FALSE) { + /* Wait for one of the threads to signal */ + pthread_mutex_lock(&servers_mutex); + while (config.quit == FALSE + && servers_waiting < thread_config.maxspareservers + && servers_waiting > thread_config.minspareservers) { + if (gettimeofday(&tv, NULL) < 0) { + return -1; + } + ts.tv_sec = tv.tv_sec + 1; + ts.tv_nsec = tv.tv_usec * 1000; + pthread_cond_timedwait(&servers_cond, &servers_mutex, &ts); + } + + if (config.quit == TRUE) + return 0; + + /* If there are not enough spare servers, create more */ + if (servers_waiting < thread_config.minspareservers) { + for (i = 0; i < thread_config.maxclients; i++) { + if (thread_ptr[i].status == T_EMPTY) { + pthread_create(&thread_ptr[i].tid, NULL, &thread_main, &thread_ptr[i]); + thread_ptr[i].status = T_WAITING; + servers_waiting++; + log(LOG_NOTICE, "Created a new thread."); + break; + } + } + } else if (servers_waiting > thread_config.maxspareservers) { + for (i = 0; i < thread_config.maxclients; i++) { + if (thread_ptr[i].status == T_WAITING) { + pthread_join(thread_ptr[i].tid, NULL); + servers_waiting--; + thread_ptr[i].status = T_EMPTY; + log(LOG_NOTICE, "Killed off a thread."); + break; + } + } + } + pthread_mutex_unlock(&servers_mutex); + } + return 0; +} + +inline int thread_listening_sock(unsigned int port) +{ + listenfd = listen_sock(port, &addrlen); + return listenfd; +} + +inline void thread_close_sock(void) +{ + close(listenfd); +} diff --git a/src/thread.h b/src/thread.h new file mode 100644 index 0000000..aea5382 --- /dev/null +++ b/src/thread.h @@ -0,0 +1,36 @@ +/* $Id: thread.h,v 1.1 2000-09-12 00:07:44 rjkaes Exp $ + * + * See 'thread.c' for more information. + * + * Copyright (C) 2000 Robert James Kaes (rjkaes@flarenet.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _TINYPROXY_THREAD_H_ +#define _TINYPROXY_THREAD_H_ + +typedef enum { + THREAD_MAXCLIENTS, + THREAD_MAXSPARESERVERS, + THREAD_MINSPARESERVERS, + THREAD_STARTSERVERS, + THREAD_MAXREQUESTSPERCHILD +} thread_config_t; + +extern int thread_pool_create(void); +extern int thread_listening_sock(unsigned int port); +extern void thread_close_sock(void); +extern int thread_main_loop(void); + +extern int thread_configure(thread_config_t type, unsigned int val); + +#endif -- cgit v1.2.3