2001-09-16 05:28:25 +08:00
|
|
|
/* $Id: thread.c,v 1.16 2001-09-15 21:28:25 rjkaes Exp $
|
2000-09-12 08:07:44 +08:00
|
|
|
*
|
|
|
|
* 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"
|
2001-09-09 02:58:37 +08:00
|
|
|
#include "utils.h"
|
2000-09-12 08:07:44 +08:00
|
|
|
|
2001-08-28 12:33:21 +08:00
|
|
|
/*
|
|
|
|
* This is the stack frame size used by all the threads. We'll start by
|
|
|
|
* setting it to 128 KB.
|
|
|
|
*/
|
2001-09-12 03:27:09 +08:00
|
|
|
#define THREAD_STACK_SIZE (1024 * 32)
|
2001-08-28 12:33:21 +08:00
|
|
|
|
2000-09-12 08:07:44 +08:00
|
|
|
static int listenfd;
|
|
|
|
static socklen_t addrlen;
|
2001-05-24 01:56:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Stores the internal data needed for each thread (connection)
|
|
|
|
*/
|
2000-09-12 08:07:44 +08:00
|
|
|
struct thread_s {
|
|
|
|
pthread_t tid;
|
|
|
|
enum { T_EMPTY, T_WAITING, T_CONNECTED } status;
|
2000-12-09 10:35:30 +08:00
|
|
|
unsigned int connects;
|
2000-09-12 08:07:44 +08:00
|
|
|
};
|
2001-05-24 01:56:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* A pointer to an array of threads. A certain number of threads are
|
|
|
|
* created when the program is started.
|
|
|
|
*/
|
2000-09-12 08:07:44 +08:00
|
|
|
static struct thread_s *thread_ptr;
|
|
|
|
static pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
2001-08-28 01:45:50 +08:00
|
|
|
/* Used to override the default statck size. */
|
|
|
|
static pthread_attr_t thread_attr;
|
|
|
|
|
2001-05-27 10:33:35 +08:00
|
|
|
static struct thread_config_s {
|
2000-09-12 08:07:44 +08:00
|
|
|
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;
|
|
|
|
|
2001-09-07 12:19:05 +08:00
|
|
|
#define SERVER_LOCK() pthread_mutex_lock(&servers_mutex)
|
|
|
|
#define SERVER_UNLOCK() pthread_mutex_unlock(&servers_mutex)
|
|
|
|
|
2000-12-09 10:35:30 +08:00
|
|
|
#define SERVER_INC() do { \
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_LOCK(); \
|
2001-09-08 14:29:04 +08:00
|
|
|
DEBUG2("INC: servers_waiting: %u", servers_waiting); \
|
2000-12-09 10:35:30 +08:00
|
|
|
servers_waiting++; \
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_UNLOCK(); \
|
2000-12-09 10:35:30 +08:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define SERVER_DEC() do { \
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_LOCK(); \
|
2000-12-09 10:35:30 +08:00
|
|
|
servers_waiting--; \
|
2001-09-08 14:29:04 +08:00
|
|
|
DEBUG2("DEC: servers_waiting: %u", servers_waiting); \
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_UNLOCK(); \
|
2000-12-09 10:35:30 +08:00
|
|
|
} while (0)
|
2000-09-12 08:07:44 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the configuration values for the various thread related settings.
|
|
|
|
*/
|
2001-09-07 12:19:05 +08:00
|
|
|
short int thread_configure(thread_config_t type, unsigned int val)
|
2000-09-12 08:07:44 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2001-09-09 02:58:37 +08:00
|
|
|
cliaddr = safemalloc(addrlen);
|
2001-09-16 05:28:25 +08:00
|
|
|
if (!cliaddr)
|
2000-09-12 08:07:44 +08:00
|
|
|
return NULL;
|
|
|
|
|
2001-09-07 03:00:47 +08:00
|
|
|
while (!config.quit) {
|
2000-09-12 08:07:44 +08:00
|
|
|
clilen = addrlen;
|
2001-09-08 14:29:04 +08:00
|
|
|
|
2000-09-12 08:07:44 +08:00
|
|
|
pthread_mutex_lock(&mlock);
|
|
|
|
connfd = accept(listenfd, cliaddr, &clilen);
|
|
|
|
pthread_mutex_unlock(&mlock);
|
2001-09-08 14:29:04 +08:00
|
|
|
|
2000-09-12 08:07:44 +08:00
|
|
|
ptr->status = T_CONNECTED;
|
2000-12-09 10:35:30 +08:00
|
|
|
|
|
|
|
SERVER_DEC();
|
2000-09-12 08:07:44 +08:00
|
|
|
|
|
|
|
handle_connection(connfd);
|
|
|
|
close(connfd);
|
|
|
|
|
2001-05-27 10:33:35 +08:00
|
|
|
if (thread_config.maxrequestsperchild != 0) {
|
2000-12-09 10:35:30 +08:00
|
|
|
ptr->connects++;
|
|
|
|
|
2001-09-08 14:29:04 +08:00
|
|
|
DEBUG2("%u connections so far...", ptr->connects);
|
|
|
|
|
2001-05-27 10:33:35 +08:00
|
|
|
if (ptr->connects >= thread_config.maxrequestsperchild) {
|
2001-09-07 12:19:05 +08:00
|
|
|
log_message(LOG_NOTICE, "Thread has reached MaxRequestsPerChild (%u > %u). Killing thread.", ptr->connects, thread_config.maxrequestsperchild);
|
2001-09-08 14:29:04 +08:00
|
|
|
|
2001-05-27 10:33:35 +08:00
|
|
|
ptr->status = T_EMPTY;
|
2001-09-08 14:29:04 +08:00
|
|
|
|
|
|
|
safefree(cliaddr);
|
|
|
|
|
2001-05-27 10:33:35 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2000-12-09 10:35:30 +08:00
|
|
|
}
|
2001-08-27 05:14:30 +08:00
|
|
|
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_LOCK();
|
2001-09-08 14:29:04 +08:00
|
|
|
if (servers_waiting >= thread_config.maxspareservers) {
|
2001-09-07 05:16:35 +08:00
|
|
|
/*
|
2001-09-08 14:29:04 +08:00
|
|
|
* There are too many spare threads, kill ourself
|
2001-09-07 05:16:35 +08:00
|
|
|
* off.
|
|
|
|
*/
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_UNLOCK();
|
|
|
|
|
|
|
|
log_message(LOG_NOTICE, "Waiting servers exceeds MaxSpareServers. Killing thread.");
|
|
|
|
|
2001-09-07 05:16:35 +08:00
|
|
|
ptr->status = T_EMPTY;
|
2001-09-08 14:29:04 +08:00
|
|
|
|
|
|
|
safefree(cliaddr);
|
2001-09-07 05:16:35 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_UNLOCK();
|
2001-09-07 05:16:35 +08:00
|
|
|
|
2001-08-27 05:14:30 +08:00
|
|
|
ptr->status = T_WAITING;
|
|
|
|
|
|
|
|
SERVER_INC();
|
2000-09-12 08:07:44 +08:00
|
|
|
}
|
2001-08-27 05:14:30 +08:00
|
|
|
|
2001-09-08 14:29:04 +08:00
|
|
|
safefree(cliaddr);
|
2001-08-27 05:14:30 +08:00
|
|
|
return NULL;
|
2000-09-12 08:07:44 +08:00
|
|
|
}
|
2001-05-24 01:56:35 +08:00
|
|
|
|
2000-09-12 08:07:44 +08:00
|
|
|
/*
|
|
|
|
* Create the initial pool of threads.
|
|
|
|
*/
|
2001-09-07 12:19:05 +08:00
|
|
|
short int thread_pool_create(void)
|
2000-09-12 08:07:44 +08:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2001-08-28 01:45:50 +08:00
|
|
|
/*
|
|
|
|
* Initialize thread_attr to contain a non-default stack size
|
2001-09-08 02:19:39 +08:00
|
|
|
* because the default on some OS's is too small. Also, make sure
|
|
|
|
* we're using a detached creation method so all resources are
|
|
|
|
* reclaimed when the thread exits.
|
2001-08-28 01:45:50 +08:00
|
|
|
*/
|
|
|
|
pthread_attr_init(&thread_attr);
|
2001-09-08 02:19:39 +08:00
|
|
|
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
|
2001-08-28 12:33:21 +08:00
|
|
|
pthread_attr_setstacksize(&thread_attr, THREAD_STACK_SIZE);
|
2001-08-28 01:45:50 +08:00
|
|
|
|
2000-09-12 08:07:44 +08:00
|
|
|
if (thread_config.maxclients == 0) {
|
2001-09-07 12:19:05 +08:00
|
|
|
log_message(LOG_ERR, "'MaxClients' must be greater than zero.");
|
2000-09-12 08:07:44 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (thread_config.startservers == 0) {
|
2001-09-07 12:19:05 +08:00
|
|
|
log_message(LOG_ERR, "'StartServers' must be greater than zero.");
|
2000-09-12 08:07:44 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2001-09-09 02:58:37 +08:00
|
|
|
thread_ptr = safecalloc((size_t)thread_config.maxclients, sizeof(struct thread_s));
|
2000-09-12 08:07:44 +08:00
|
|
|
if (!thread_ptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (thread_config.startservers > thread_config.maxclients) {
|
2001-09-07 12:19:05 +08:00
|
|
|
log_message(LOG_WARNING, "Can not start more than 'MaxClients' servers. Starting %u servers instead.", thread_config.maxclients);
|
2000-09-12 08:07:44 +08:00
|
|
|
thread_config.startservers = thread_config.maxclients;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < thread_config.startservers; i++) {
|
|
|
|
thread_ptr[i].status = T_WAITING;
|
2001-08-28 01:45:50 +08:00
|
|
|
pthread_create(&thread_ptr[i].tid, &thread_attr, &thread_main, &thread_ptr[i]);
|
2000-09-12 08:07:44 +08:00
|
|
|
}
|
2001-09-07 03:00:47 +08:00
|
|
|
servers_waiting = thread_config.startservers;
|
|
|
|
|
2000-09-12 08:07:44 +08:00
|
|
|
for (i = thread_config.startservers; i < thread_config.maxclients; i++) {
|
|
|
|
thread_ptr[i].status = T_EMPTY;
|
2000-12-09 10:35:30 +08:00
|
|
|
thread_ptr[i].connects = 0;
|
2000-09-12 08:07:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-09-07 05:16:35 +08:00
|
|
|
* Keep the proper number of servers running. This is the birth of the
|
|
|
|
* servers. It monitors this at least once a second.
|
2000-09-12 08:07:44 +08:00
|
|
|
*/
|
2001-09-07 12:19:05 +08:00
|
|
|
void thread_main_loop(void)
|
2000-09-12 08:07:44 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2000-12-08 11:35:07 +08:00
|
|
|
/* If there are not enough spare servers, create more */
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_LOCK();
|
2000-12-08 11:35:07 +08:00
|
|
|
if (servers_waiting < thread_config.minspareservers) {
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_UNLOCK();
|
|
|
|
|
2000-12-08 11:35:07 +08:00
|
|
|
for (i = 0; i < thread_config.maxclients; i++) {
|
|
|
|
if (thread_ptr[i].status == T_EMPTY) {
|
2001-08-28 01:45:50 +08:00
|
|
|
pthread_create(&thread_ptr[i].tid, &thread_attr, &thread_main, &thread_ptr[i]);
|
2000-12-08 11:35:07 +08:00
|
|
|
thread_ptr[i].status = T_WAITING;
|
2001-05-24 01:56:35 +08:00
|
|
|
thread_ptr[i].connects = 0;
|
2000-12-09 10:35:30 +08:00
|
|
|
|
|
|
|
SERVER_INC();
|
|
|
|
|
2001-09-07 12:19:05 +08:00
|
|
|
log_message(LOG_NOTICE, "Waiting servers is less than MinSpareServers. Creating new thread.");
|
2001-09-08 14:29:04 +08:00
|
|
|
|
2000-12-08 11:35:07 +08:00
|
|
|
break;
|
2000-09-12 08:07:44 +08:00
|
|
|
}
|
2000-12-08 11:35:07 +08:00
|
|
|
}
|
2000-09-12 08:07:44 +08:00
|
|
|
}
|
2001-09-07 12:19:05 +08:00
|
|
|
SERVER_UNLOCK();
|
2000-09-12 08:07:44 +08:00
|
|
|
}
|
|
|
|
|
2001-09-07 12:19:05 +08:00
|
|
|
int thread_listening_sock(uint16_t port)
|
2000-09-12 08:07:44 +08:00
|
|
|
{
|
|
|
|
listenfd = listen_sock(port, &addrlen);
|
|
|
|
return listenfd;
|
|
|
|
}
|
|
|
|
|
2001-09-07 12:19:05 +08:00
|
|
|
void thread_close_sock(void)
|
2000-09-12 08:07:44 +08:00
|
|
|
{
|
|
|
|
close(listenfd);
|
|
|
|
}
|