modules/th/thread.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- log_print
- TH_acquire_read_lock
- TH_release_read_lock
- TH_acquire_write_lock
- TH_release_write_lock
- TH_init_read_write_lock
- TH_get_id
- TH_to_string
- TH_do_whois
- TH_do_mirror
- TH_do_config
- TH_hdl_signal
- main_thread
- TH_run
- TH_run1
- TH_run2
- TH_watchdog
- do_watchdog
/***************************************
$Revision: 1.19 $
Example code: A thread.
Status: NOT REVUED, NOT TESTED
Authors: Chris Ottrey
Joao Damas
+html+ <DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL>
+html+ </DL>
******************/ /******************
Modification History:
ottrey (02/03/1999) Created.
ottrey (08/03/1999) Modified.
ottrey (17/06/1999) Stripped down.
joao (22/06/1999) Redid thread startup
******************/ /******************
Copyright (c) 1999 RIPE NCC
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of the author not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
***************************************/
#include <pthread.h> /* Posix thread library */
#include <stdio.h>
#include <strings.h>
#include "thread.h"
#include "socket.h"
#include "protocol_whois.h"
#include "protocol_config.h"
#include "protocol_mirror.h"
#include "constants.h"
#include "server.h"
#include "memwrap.h"
/*+ String sizes +*/
#define STR_S 63
#define STR_M 255
#define STR_L 1023
#define STR_XL 4095
#define STR_XXL 16383
/*+ Mutex lock. Used for synchronizing changes. +*/
pthread_mutex_t Whois_thread_count_lock;
pthread_mutex_t Config_thread_count_lock;
pthread_mutex_t Mirror_thread_count_lock;
/*+ The number of threads. +*/
int Whois_thread_count;
int Config_thread_count;
int Mirror_thread_count;
typedef struct th_args {
void *function;
int sock;
} th_args;
/* Some static declarations */
/* This is a watchdog function/thread */
/* It is started by TH_watchdog function */
static void do_watchdog(void *arg);
/* Logging results */
static void log_print(const char *arg) {
/* [<][>][^][v][top][bottom][index][help] */
FILE *logf;
if (CO_get_thread_logging() == 1) {
if (strcmp(CO_get_thread_logfile(), "stdout") == 0) {
printf(arg);
}
else {
logf = fopen(CO_get_thread_logfile(), "a");
fprintf(logf, arg);
fclose(logf);
}
}
} /* log_print() */
/* TH_acquire_read_lock() */
/*++++++++++++++++++++++++++++++++++++++
Aquire a readers lock.
rw_lock_t *prw_lock Readers writers lock.
Reference: "Multithreaded Programming Techniques - Prasad p.192"
More:
+html+ <PRE>
Author:
ottrey
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_acquire_read_lock(rw_lock_t *prw_lock) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_mutex_lock(&prw_lock->rw_mutex);
while (prw_lock->rw_count < 0) {
pthread_cond_wait(&prw_lock->rw_cond, &prw_lock->rw_mutex);
}
++prw_lock->rw_count;
pthread_mutex_unlock(&prw_lock->rw_mutex);
} /* TH_acquire_read_lock() */
/* TH_release_read_lock() */
/*++++++++++++++++++++++++++++++++++++++
Release a readers lock.
rw_lock_t *prw_lock Readers writers lock.
Reference: "Multithreaded Programming Techniques - Prasad p.192"
More:
+html+ <PRE>
Author:
ottrey
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_release_read_lock(rw_lock_t *prw_lock) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_mutex_lock(&prw_lock->rw_mutex);
--prw_lock->rw_count;
if (!prw_lock->rw_count) {
pthread_cond_signal(&prw_lock->rw_cond);
}
pthread_mutex_unlock(&prw_lock->rw_mutex);
} /* TH_release_read_lock() */
/* TH_acquire_write_lock() */
/*++++++++++++++++++++++++++++++++++++++
Aquire a writers lock.
rw_lock_t *prw_lock Readers writers lock.
Reference: "Multithreaded Programming Techniques - Prasad p.192"
More:
+html+ <PRE>
Author:
ottrey
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_acquire_write_lock(rw_lock_t *prw_lock) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_mutex_lock(&prw_lock->rw_mutex);
while (prw_lock->rw_count != 0) {
pthread_cond_wait(&prw_lock->rw_cond, &prw_lock->rw_mutex);
}
prw_lock->rw_count = -1;
pthread_mutex_unlock(&prw_lock->rw_mutex);
} /* TH_acquire_write_lock() */
/* TH_release_write_lock() */
/*++++++++++++++++++++++++++++++++++++++
Release a writers lock.
rw_lock_t *prw_lock Readers writers lock.
Reference: "Multithreaded Programming Techniques - Prasad p.192"
More:
+html+ <PRE>
Author:
ottrey
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_release_write_lock(rw_lock_t *prw_lock) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_mutex_lock(&prw_lock->rw_mutex);
prw_lock->rw_count = 0;
pthread_mutex_unlock(&prw_lock->rw_mutex);
pthread_cond_broadcast(&prw_lock->rw_cond);
} /* TH_release_write_lock() */
/* TH_init_read_write_lock() */
/*++++++++++++++++++++++++++++++++++++++
Initialize a readers/writers lock.
rw_lock_t *prw_lock Readers writers lock.
Side effect: the lock is set to open(?)
Reference: "Multithreaded Programming Techniques - Prasad p.192"
More:
+html+ <PRE>
Author:
ottrey
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_init_read_write_lock(rw_lock_t *prw_lock) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_mutex_init(&prw_lock->rw_mutex, NULL);
pthread_cond_init(&prw_lock->rw_cond, NULL);
prw_lock->rw_count = 0;
} /* TH_init_read_write_lock() */
int TH_get_id(void) {
/* [<][>][^][v][top][bottom][index][help] */
return (int)pthread_self();
} /* TH_get_id() */
/* TH_to_string() */
char *TH_to_string(void) {
/* [<][>][^][v][top][bottom][index][help] */
char *thread_info;
char tmp[STR_L];
char thread_info_buffer[STR_XL];
strcpy(thread_info_buffer, "Thread = { ");
sprintf(tmp, "[pthread_self] = \"%d\" ", pthread_self());
strcat(thread_info_buffer, tmp);
/*
thread_name = (char *)pthread_getspecific(Name);
if (thread_name == NULL ) {
sprintf(tmp, "[Name] = \"%s\" ", "didn't work!");
}
else {
sprintf(tmp, "[Name] = \"%s\" ", thread_name);
}
strcat(thread_info_buffer, tmp);
*/
strcat(thread_info_buffer, "}");
dieif( wr_malloc((void **)&thread_info,
strlen(thread_info_buffer)+1) != UT_OK);
strcpy(thread_info, thread_info_buffer);
return thread_info;
} /* TH_to_string() */
/* TH_do_whois() */
/*++++++++++++++++++++++++++++++++++++++
Handle whois connections.
void *arg The socket to connect to. (It has to be passed in this way for this thread routine.)
More:
+html+ <PRE>
Author:
joao
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_do_whois(void *arg) {
/* [<][>][^][v][top][bottom][index][help] */
int sock = (int)arg;
ER_dbg_va(FAC_TH, ASP_TH_NEW,
"Whois: Child thread [%d]: Socket number = %d",
pthread_self(), sock);
/* Use a mutex to update the global whois thread counter. */
pthread_mutex_lock(&Whois_thread_count_lock);
Whois_thread_count++;
ER_dbg_va(FAC_TH, ASP_TH_NEW,
"Whois_thread_count++=%d", Whois_thread_count);
pthread_mutex_unlock(&Whois_thread_count_lock);
PW_interact(sock);
/* Use a mutex to update the global whois thread counter. */
pthread_mutex_lock(&Whois_thread_count_lock);
Whois_thread_count--;
ER_dbg_va(FAC_TH, ASP_TH_NEW,
"Whois_thread_count--=%d", Whois_thread_count);
pthread_mutex_unlock(&Whois_thread_count_lock);
pthread_exit((void *)0);
} /* TH_do_whois() */
/* TH_do_mirror() */
/*++++++++++++++++++++++++++++++++++++++
Handle NRTM connections.
void *arg The socket to connect to. (It has to be passed in this way for this thread routine.)
More:
+html+ <PRE>
Author:
joao
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_do_mirror(void *arg) {
/* [<][>][^][v][top][bottom][index][help] */
int sock = (int)arg;
char print_buf[STR_M];
sprintf(print_buf, "NRTM: Child thread [%d]: Socket number = %d\n", pthread_self(), sock); log_print(print_buf); strcpy(print_buf, "");
/* Use a mutex to update the global mirror thread counter. */
pthread_mutex_lock(&Mirror_thread_count_lock);
Mirror_thread_count++;
sprintf(print_buf, "Mirror_thread_count++=%d\n", Mirror_thread_count); log_print(print_buf); strcpy(print_buf, "");
pthread_mutex_unlock(&Mirror_thread_count_lock);
PM_interact(sock);
/* Use a mutex to update the global mirror thread counter. */
pthread_mutex_lock(&Mirror_thread_count_lock);
Mirror_thread_count--;
sprintf(print_buf, "Mirror_thread_count--=%d\n", Mirror_thread_count); log_print(print_buf); strcpy(print_buf, "");
pthread_mutex_unlock(&Mirror_thread_count_lock);
pthread_exit((void *)0);
} /* TH_do_mirror() */
/* TH_do_config() */
/*++++++++++++++++++++++++++++++++++++++
Handle config connections.
void *arg The socket to connect to. (It has to be passed in this way for this
thread routine.)
More:
+html+ <PRE>
Author:
joao
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_do_config(void *arg) {
/* [<][>][^][v][top][bottom][index][help] */
int sock = (int)arg;
char print_buf[STR_M];
sprintf(print_buf, "Config: Child thread [%d]: Socket number = %d\n", pthread_self(), sock); log_print(print_buf); strcpy(print_buf, "");
/*
printf("Hi there, there is nothing to configure yet\nBye..... :-)\n");
fflush(NULL);
SK_close(sock);
*/
PC_interact(sock);
pthread_exit((void *)0);
} /* TH_do_config() */
/* TH_hdl_signal() */
/*++++++++++++++++++++++++++++++++++++++
Handle signals.
Changes the flags:
do_nrtm
do_update
do_whoisd
More:
+html+ <PRE>
Author:
andrei
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_hdl_signal() {
/* [<][>][^][v][top][bottom][index][help] */
char print_buf[STR_M];
sigset_t sset;
int sigReceived;
int do_update;
sigemptyset(&sset);
sigaddset(&sset, SIGTERM);
sigaddset(&sset, SIGINT);
/* This is a bit confusing, but is needed */
/* For more information on signal handling in */
/* threads see for example "Multithreading Programming */
/* Techniques" by Shashi Prasad, ISBN 0-07-912250-7, pp. 94-101 */
pthread_sigmask(SIG_BLOCK, &sset, NULL);
/* fprintf(stderr, "Signal handler installed\n");*/
for(;;)
{
sigwait(&sset, &sigReceived);
sprintf(print_buf, "Signal received [%d]\n", sigReceived);
log_print(print_buf); strcpy(print_buf, "");
/* fprintf(stderr, "Signal received [%d]\n", sigReceived); */
switch (sigReceived)
{
case SIGINT:
/* SIGINT stops all servers */
SV_shutdown();
pthread_exit((void *)0);
break;
case SIGTERM:
/* SIGTERM will switch the updates on and off */
do_update=CO_get_do_update();
if(do_update)do_update=0; else do_update=1;
sprintf(print_buf, "%d", do_update);
CO_set_const("UD.do_update", print_buf);
if(do_update)
sprintf(print_buf, "Starting updates\n");
else
sprintf(print_buf, "Stopping updates\n");
log_print(print_buf); strcpy(print_buf, "");
/* fprintf(stderr, "Stopping updates (SIGTERM received)\n"); */
break;
}
}
} /* TH_hdl_signal() */
/* main_thread() */
/*++++++++++++++++++++++++++++++++++++++
Waits for an incoming connection on the and spawns a new thread to handle it.
void *arg Pointer to a struct containing the socket to talk to the client and
the function to call depending on the incoming connection.
More:
+html+ <PRE>
Author:
ottrey
joao
andrei (do_server)
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
static void *main_thread(void *arg) {
/* [<][>][^][v][top][bottom][index][help] */
th_args *args = (th_args *)arg;
pthread_t tid;
pthread_attr_t attr;
int connected_socket;
int do_server;
while(do_server=CO_get_do_server()) {
connected_socket = SK_accept_connection(args->sock);
if(connected_socket==-1) break;
ER_dbg_va(FAC_TH, ASP_TH_NEW, "Starting a new thread");
/* Start a new thread. */
pthread_attr_init(&attr); /* initialize attr with default attributes */
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, (void *(*)(void *))(args->function), (void *)connected_socket);
}
ER_dbg_va(FAC_TH, ASP_TH_NEW, "Exiting from the main thread");
} /* main_thread() */
/* TH_run() */
/*++++++++++++++++++++++++++++++++++++++
This is the routine that creates the main threads.
int sock The socket to connect to.
void * do_function The function to call for each type of service
More:
+html+ <PRE>
Author:
ottrey
joao
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_run(int sock, void *do_function(void *)) {
/* [<][>][^][v][top][bottom][index][help] */
th_args *args;
pthread_t tid;
pthread_attr_t attr;
dieif( wr_calloc((void **)&args,1,sizeof(th_args)) != UT_OK);
args->function=(void *)do_function;
args->sock=sock;
/* pthread_mutex_init(&Whois_thread_count_lock,NULL); */
/* Start a new thread. */
pthread_attr_init(&attr); /* initialize attr with default attributes */
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, main_thread, (void *)args);
} /* TH_run() */
/*++++++++++++++++++++++++++++++++++++++
This is the routine that creates 1 main thread.
int sock The socket to listen to.
void * do_function The function to call for each type of service
More:
+html+ <PRE>
Author:
ottrey
joao
andrei
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_run1(int sock, void *do_function(void *) ) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_t tid;
pthread_attr_t attr;
/* Start a new thread. */
pthread_attr_init(&attr); /* initialize attr with default attributes */
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, (void *(*)(void *))do_function, (void *)sock);
} /* TH_run() */
void TH_run2(void *function(void *)) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_t tid;
pthread_attr_t attr;
/* Start a new thread. */
pthread_attr_init(&attr); /* initialize attr with default attributes */
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, (void *(*)(void *))function, (void *)0);
} /* TH_run2() */
/*++++++++++++++++++++++++++++++++++++++
This is the routine that creates a watchdog thread.
The watchdog will cancel (pthread_cancel()) the calling thread in case the
socket is closed by the client (its read-half is closed). The calling
thread should make necessaruy preparations when calling the watchdog:
- the socket should be connected
- cancellation points and cleanup routines should be defined
In case the connection is closed by the calling thread itself, the
watchdog just exits and no action against the calling thread is performed.
wd_args - a pointer to wd_args_t structure containing
data about socket and thread ID
More:
+html+ <PRE>
Author:
ottrey
joao
andrei
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
void TH_watchdog(wd_args_t *wd_args) {
/* [<][>][^][v][top][bottom][index][help] */
pthread_t tid;
pthread_attr_t attr;
/* Start a new thread. */
pthread_attr_init(&attr); /* initialize attr with default attributes */
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, (void *(*)(void *))do_watchdog, (void *)wd_args);
}
/*++++++++++++++++++++++++++++++++++++++
The watchdog thread itself
The watchdog thread makes select() on the connected socket waiting until it
becomes readable. If this happens as a result of some input, it'll simply
dump it. Otherwise, this indicates that the client has closed the
connection. In this case watchdog will cancel (pthread_cancel()) the whois
thread (which in its turn will kill (mysql_kill()) mysql thread as part of
its cleanup routine).
More:
+html+ <PRE>
Author:
andrei
+html+ </PRE>
++++++++++++++++++++++++++++++++++++++*/
static void do_watchdog(void *arg) {
/* [<][>][^][v][top][bottom][index][help] */
wd_args_t *wd_args = (wd_args_t *)arg;
int socket;
pthread_t tid;
int nready;
int n;
fd_set rset;
char buff[STR_S];
socket = wd_args->connected_socket;
tid = wd_args->tid;
FD_ZERO(&rset);
FD_SET(socket, &rset);
while ((nready=select(socket+1, &rset, NULL, NULL, NULL))!=-1) {
/* There was some input or client half of connection was closed */
/* Check for the latter */
if (( n=read(socket, buff, sizeof(buff))) == 0) {
/* Connection was closed by client */
/* Now send a cancellation request to the whois thread. */
/* mysql thread will be terminated by thread cleanup routine */
/* The only possible error is ESRCH, so we do not care about */
pthread_cancel(tid);
/* Exit the watchdog thread, passing NULL as we don't expect pthread_join() */
pthread_exit(NULL);
}
/* Otherwise dump input and continue */
}
/* the only reason that we are here is that the socket has been */
/* closed by the whois thread and not valid. Just exit the watchdog, */
/* passing NULL as we don't expect pthread_join() */
pthread_exit(NULL);
}