modules/th/thread.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. log_print
  2. TH_acquire_read_lock
  3. TH_release_read_lock
  4. TH_acquire_write_lock
  5. TH_release_write_lock
  6. TH_init_read_write_lock
  7. TH_get_id
  8. TH_to_string
  9. TH_do_whois
  10. TH_do_mirror
  11. TH_do_config
  12. TH_hdl_signal
  13. main_thread
  14. TH_run
  15. TH_run1
  16. TH_run2
  17. TH_watchdog
  18. 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);
  
}  

/* [<][>][^][v][top][bottom][index][help] */