No longer using a threading model; so these files have been replaced by
the child.c/child.h files (which use a pre-forked model.)
This commit is contained in:
parent
1fda8899b1
commit
c35e56ae66
400
src/thread.c
400
src/thread.c
@ -1,400 +0,0 @@
|
||||
/* $Id: thread.c,v 1.31 2002-05-24 04:45:32 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 "heap.h"
|
||||
#include "log.h"
|
||||
#include "reqs.h"
|
||||
#include "sock.h"
|
||||
#include "thread.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
* This is the stack frame size used by all the threads. We'll start by
|
||||
* setting it to 32 KB.
|
||||
*/
|
||||
#define THREAD_STACK_SIZE (1024 * 32)
|
||||
|
||||
static int listenfd;
|
||||
static socklen_t addrlen;
|
||||
|
||||
/*
|
||||
* Stores the internal data needed for each thread (connection)
|
||||
*/
|
||||
struct thread_s {
|
||||
pthread_t tid;
|
||||
enum { T_EMPTY, T_WAITING, T_CONNECTED } status;
|
||||
unsigned int connects;
|
||||
};
|
||||
|
||||
/*
|
||||
* A pointer to an array of threads. A certain number of threads are
|
||||
* created when the program is started.
|
||||
*/
|
||||
static struct thread_s *thread_ptr;
|
||||
static pthread_mutex_t mlock;
|
||||
|
||||
#define ACCEPT_LOCK() do { \
|
||||
int accept_lock_ret = pthread_mutex_lock(&mlock); \
|
||||
assert(accept_lock_ret == 0); \
|
||||
} while (0)
|
||||
#define ACCEPT_UNLOCK() do { \
|
||||
int accept_lock_ret = pthread_mutex_unlock(&mlock); \
|
||||
assert(accept_lock_ret == 0); \
|
||||
} while (0)
|
||||
|
||||
/* Used to override the default stack size. */
|
||||
static pthread_attr_t thread_attr;
|
||||
|
||||
static struct thread_config_s {
|
||||
unsigned int maxclients, maxrequestsperchild;
|
||||
unsigned int maxspareservers, minspareservers, startservers;
|
||||
} thread_config;
|
||||
|
||||
static int servers_waiting = 0; /* servers waiting for a connection */
|
||||
static pthread_mutex_t servers_mutex;
|
||||
|
||||
#define SERVER_COUNT_LOCK() do { \
|
||||
int servers_mutex_ret = pthread_mutex_lock(&servers_mutex); \
|
||||
assert(servers_mutex_ret == 0); \
|
||||
} while (0)
|
||||
#define SERVER_COUNT_UNLOCK() do { \
|
||||
int servers_mutex_ret = pthread_mutex_unlock(&servers_mutex); \
|
||||
assert(servers_mutex_ret == 0); \
|
||||
} while (0)
|
||||
|
||||
#define SERVER_INC() do { \
|
||||
SERVER_COUNT_LOCK(); \
|
||||
++servers_waiting; \
|
||||
DEBUG2("INC: servers_waiting: %d", servers_waiting); \
|
||||
SERVER_COUNT_UNLOCK(); \
|
||||
} while (0)
|
||||
|
||||
#define SERVER_DEC() do { \
|
||||
SERVER_COUNT_LOCK(); \
|
||||
--servers_waiting; \
|
||||
DEBUG2("DEC: servers_waiting: %d", servers_waiting); \
|
||||
SERVER_COUNT_UNLOCK(); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Set the configuration values for the various thread related settings.
|
||||
*/
|
||||
short 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;
|
||||
|
||||
#ifdef HAVE_PTHREAD_CANCEL
|
||||
/* Set the cancellation type to immediate. */
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||
#endif
|
||||
|
||||
ptr = (struct thread_s *) arg;
|
||||
|
||||
cliaddr = safemalloc(addrlen);
|
||||
if (!cliaddr)
|
||||
return NULL;
|
||||
|
||||
ptr->connects = 0;
|
||||
|
||||
while (!config.quit) {
|
||||
ptr->status = T_WAITING;
|
||||
|
||||
clilen = addrlen;
|
||||
|
||||
/*
|
||||
* Check to see if the program is shutting down.
|
||||
*/
|
||||
if (config.quit)
|
||||
break;
|
||||
|
||||
ACCEPT_LOCK();
|
||||
connfd = accept(listenfd, cliaddr, &clilen);
|
||||
ACCEPT_UNLOCK();
|
||||
|
||||
/*
|
||||
* Make sure no error occurred...
|
||||
*/
|
||||
if (connfd < 0) {
|
||||
/*
|
||||
* Accept could return an "error" if it was
|
||||
* interrupted by a signal (like when the program
|
||||
* should be killed. :)
|
||||
*/
|
||||
if (config.quit)
|
||||
break;
|
||||
|
||||
log_message(LOG_ERR, "Accept returned an error (%s) ... retrying.", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr->status = T_CONNECTED;
|
||||
|
||||
SERVER_DEC();
|
||||
|
||||
handle_connection(connfd);
|
||||
|
||||
if (thread_config.maxrequestsperchild != 0) {
|
||||
ptr->connects++;
|
||||
|
||||
DEBUG2("%u connections so far...", ptr->connects);
|
||||
|
||||
if (ptr->connects >= thread_config.maxrequestsperchild) {
|
||||
log_message(LOG_NOTICE,
|
||||
"Thread has reached MaxRequestsPerChild (%u > %u). Killing thread.",
|
||||
ptr->connects,
|
||||
thread_config.maxrequestsperchild);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SERVER_COUNT_LOCK();
|
||||
if (servers_waiting > thread_config.maxspareservers) {
|
||||
/*
|
||||
* There are too many spare threads, kill ourself
|
||||
* off.
|
||||
*/
|
||||
log_message(LOG_NOTICE,
|
||||
"Waiting servers (%d) exceeds MaxSpareServers (%d). Killing thread.",
|
||||
servers_waiting, thread_config.maxspareservers);
|
||||
SERVER_COUNT_UNLOCK();
|
||||
|
||||
break;
|
||||
} else {
|
||||
SERVER_COUNT_UNLOCK();
|
||||
}
|
||||
|
||||
SERVER_INC();
|
||||
}
|
||||
|
||||
ptr->status = T_EMPTY;
|
||||
|
||||
safefree(cliaddr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the initial pool of threads.
|
||||
*/
|
||||
short int
|
||||
thread_pool_create(void)
|
||||
{
|
||||
unsigned int i;
|
||||
int pthread_ret;
|
||||
#if 0
|
||||
pthread_mutexattr_t mutexattr;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize thread_attr to contain a non-default stack size
|
||||
* 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.
|
||||
*/
|
||||
pthread_attr_init(&thread_attr);
|
||||
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setstacksize(&thread_attr, THREAD_STACK_SIZE);
|
||||
|
||||
#if 0
|
||||
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
#endif
|
||||
pthread_mutex_init(&mlock, NULL);
|
||||
pthread_mutex_init(&servers_mutex, NULL);
|
||||
|
||||
if (thread_config.maxclients == 0) {
|
||||
log_message(LOG_ERR,
|
||||
"thread_pool_create: \"MaxClients\" must be greater than zero.");
|
||||
return -1;
|
||||
}
|
||||
if (thread_config.startservers == 0) {
|
||||
log_message(LOG_ERR,
|
||||
"thread_pool_create: \"StartServers\" must be greater than zero.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread_ptr = safecalloc((size_t) thread_config.maxclients,
|
||||
sizeof(struct thread_s));
|
||||
if (!thread_ptr) {
|
||||
log_message(LOG_ERR, "Could not allocate memory for threads.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (thread_config.startservers > thread_config.maxclients) {
|
||||
log_message(LOG_WARNING,
|
||||
"Can not start more than \"MaxClients\" servers. Starting %u servers instead.",
|
||||
thread_config.maxclients);
|
||||
thread_config.startservers = thread_config.maxclients;
|
||||
}
|
||||
|
||||
for (i = 0; i < thread_config.maxclients; i++) {
|
||||
thread_ptr[i].status = T_EMPTY;
|
||||
thread_ptr[i].connects = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < thread_config.startservers; i++) {
|
||||
DEBUG2("Trying to create thread %d of %d", i + 1, thread_config.startservers);
|
||||
thread_ptr[i].status = T_WAITING;
|
||||
pthread_ret = pthread_create(&thread_ptr[i].tid, &thread_attr,
|
||||
&thread_main, &thread_ptr[i]);
|
||||
if (pthread_ret != 0) {
|
||||
log_message(LOG_WARNING,
|
||||
"Could not create thread number %d of %d: %s",
|
||||
i, thread_config.startservers,
|
||||
strerror(pthread_ret));
|
||||
return -1;
|
||||
} else {
|
||||
log_message(LOG_INFO,
|
||||
"Creating thread number %d of %d ...",
|
||||
i + 1, thread_config.startservers);
|
||||
|
||||
SERVER_INC();
|
||||
}
|
||||
}
|
||||
|
||||
log_message(LOG_INFO, "Finished creating all threads.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep the proper number of servers running. This is the birth of the
|
||||
* servers. It monitors this at least once a second.
|
||||
*/
|
||||
void
|
||||
thread_main_loop(void)
|
||||
{
|
||||
int i;
|
||||
int pthread_ret;
|
||||
|
||||
while (1) {
|
||||
if (config.quit)
|
||||
return;
|
||||
|
||||
/* If there are not enough spare servers, create more */
|
||||
SERVER_COUNT_LOCK();
|
||||
if (servers_waiting < thread_config.minspareservers) {
|
||||
log_message(LOG_NOTICE,
|
||||
"Waiting servers (%d) is less than MinSpareServers (%d). Creating new thread.",
|
||||
servers_waiting, thread_config.minspareservers);
|
||||
|
||||
SERVER_COUNT_UNLOCK();
|
||||
|
||||
for (i = 0; i < thread_config.maxclients; i++) {
|
||||
if (thread_ptr[i].status == T_EMPTY) {
|
||||
thread_ptr[i].status = T_WAITING;
|
||||
pthread_ret = pthread_create(&thread_ptr[i].tid,
|
||||
&thread_attr,
|
||||
&thread_main,
|
||||
&thread_ptr[i]);
|
||||
if (pthread_ret != 0) {
|
||||
log_message(LOG_NOTICE,
|
||||
"Could not create thread: %s",
|
||||
strerror(pthread_ret));
|
||||
|
||||
thread_ptr[i].status = T_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
SERVER_INC();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SERVER_COUNT_UNLOCK();
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
|
||||
/* Handle log rotation if it was requested */
|
||||
if (log_rotation_request) {
|
||||
rotate_log_files();
|
||||
log_rotation_request = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through all the non-empty threads and cancel them.
|
||||
*/
|
||||
#ifdef HAVE_PTHREAD_CANCEL
|
||||
void
|
||||
thread_kill_threads(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < thread_config.maxclients; i++) {
|
||||
if (thread_ptr[i].status != T_EMPTY)
|
||||
pthread_cancel(thread_ptr[i].tid);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void
|
||||
thread_kill_threads(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
thread_listening_sock(uint16_t port)
|
||||
{
|
||||
listenfd = listen_sock(port, &addrlen);
|
||||
return listenfd;
|
||||
}
|
||||
|
||||
void
|
||||
thread_close_sock(void)
|
||||
{
|
||||
close(listenfd);
|
||||
}
|
37
src/thread.h
37
src/thread.h
@ -1,37 +0,0 @@
|
||||
/* $Id: thread.h,v 1.4 2002-01-25 00:01:45 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 short int thread_pool_create(void);
|
||||
extern int thread_listening_sock(uint16_t port);
|
||||
extern void thread_close_sock(void);
|
||||
extern void thread_main_loop(void);
|
||||
extern void thread_kill_threads(void);
|
||||
|
||||
extern short int thread_configure(thread_config_t type, unsigned int val);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user