pthreads and signals

I have a c++ code that creates two threads. The program is running constantly, so I wanted to stop is by ctr+c (as opposed to kill, because I wanted to save some files and other logging stuff). I also wanted to be notified of segmentation faults. What I did was  something like this:

//in my main

struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGSEGV, &sa, NULL);
 
s = new X(s1.c_str());
e = new Y("config.ini", s1.c_str());
 
//this is actually the one that creates the threads
thread_X=e->start();  
thread_Y=s->start();

//and then I wait on the threads to finish
pthread_join(thread_X, NULL);
pthread_join(thread_Y, NULL);

//and the signal handler is ..

void signal_handler(int sig)
{
 
  if (sig==SIGINT)
    cout << "So you don't like me any more? Why do you interrupting me??::" << endl;
  else if (sig ==SIGSEGV)
    cout << "Go and check why I segmented myself. It is your fault, you know::"  << endl;
  else
    cout << "RECEIVED SIGNAL NUMBER " << sig << "Go and check why"  << endl;

  //detach the two threads
  cout << "Stopping X" << endl;
  if (!pthread_cancel(thread_X))
    cout << "X stopped" << endl;
  cout << "Stopping Y" << endl;
  if (!pthread_cancel(thread_Y))
    cout << "Y stopped" << endl;

  delete e;
  cout << "e Deleted" << endl;
  delete s;
  cout << "s Deleted" << endl;
  exit(0);
}

The problem is when my program is running and I enter ctrl-C, then the program stops, but sometimes I have problems that I don't understand.when it is running I check the process and it has a pid of x, let's say. Once I do the ctrl+c, the program seems to stop, but when I check the proceses, the program is still there, with a pid of x+10 (all the time this happens), with the start time the same as the original process but a running time of 0.

I also get sometimes the signal handler called with the sigsegv when I try to do the ctr+c.

Any suggestions?

I looked around, and it is recommended in a lot of places not to mix threads with signals, and they recommend using sigwait. I thought this may be a solution and I tried it , ofcourse I got problems with that too:-)

THe problem I have with sigwait is that I was able to catch only asynchronous signals such as SIGINT only, and didn't manage how to deal with synchronous signals such as SIGSEGV. FOr example, I was playing with this code below. I deliberatley do a division by zero when the counter reaches ten in the threads, as you can see. The concerned thread will stop counting after 10, but the signal handler is never called. Why?

Any other way of doing what I wanted to do (stated at the begining of this question) easily??

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
/* global variable used to indicate what signal<\n>
 * (if any) has been caught
 */
int handled_signal = -1;
/* mutex to be used whenever accessing the above
 * global data */
pthread_mutex_t sig_mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_t dummy1, dummy2, dummy3;


void signal_handler_1(int sig)
{
  printf("dummy1 caused SIGSEGV\n");
  exit(-1);
}

void signal_handler_2(int sig)
{
  printf("dummy2 caused SIGSEGV\n");
  exit(-1);
}

void signal_handler_3(int sig)
{
  printf("dummy3 caused SIGSEGV\n");
  exit(-1);
}

void* sig_handler_2(void* arg)
{
  int id = *((int *)arg);
  sigset_t sigseg;


  int counter=0;
  for (;;)
    {
      sleep(id);
      printf("%d:%d\n", id, ++counter);
      int j;
      if (counter == 10)
      {
        j = counter /(counter -10); //so that we cause a sigsegv
      }
    }
  return (void *)0;
}

void* sig_handler( void* arg )
{
  sigset_t signal_set;
  int sig;
  for(;;)
    {
      /* wait for any and all signals */
      sigfillset( &signal_set );
      sigwait( &signal_set, &sig );
      /* when we get this far, we've
       * caught a signal */
      switch( sig )
      {
        /* whatever you need to do on
         * SIGQUIT */
      case SIGQUIT:  
        pthread_mutex_lock(&sig_mutex);
        handled_signal = SIGQUIT;
        pthread_mutex_unlock(&sig_mutex);
        break;
        /* whatever you need to do on
         * SIGINT */
      case SIGINT:
        pthread_mutex_lock(&sig_mutex);
        handled_signal = SIGINT;
        pthread_mutex_unlock(&sig_mutex);
        printf("So you don't like me any more? Why do you interrupting me??\n");

        printf("Stopping dummy1\n");
        if (!pthread_cancel(dummy1))
          printf("dummy1 stopped\n");

        printf("Stopping dummy2\n");
        if (!pthread_cancel(dummy2))
          printf("dummy2 stopped\n");

        printf("Stopping dummy3\n");
        if (!pthread_cancel(dummy3))
          printf("dummy3 stopped\n");

        exit(0);
        break;
        /* whatever you need to do for
         * other signals */
      default:  
        pthread_mutex_lock(&sig_mutex);
        handled_signal = 0;
        pthread_mutex_unlock(&sig_mutex);
        break;
      }
    }
  return (void*)0;
}

struct sigaction sa;

int main(void )
{
  struct sigaction sa;

  sigset_t signal_set;
  pthread_t sig_thread;
  /* block all signals, except sigsev */
  sigfillset( &signal_set );

  sigdelset( &signal_set, SIGSEGV);

  pthread_sigmask( SIG_BLOCK, &signal_set, NULL );
  pthread_create( &sig_thread, NULL, sig_handler, NULL );

 
  int i1=1, i2=2, i3=3;

//I was thinking this will take care of SIGSEGV, but not
  sa.sa_handler = signal_handler_1;
  sigemptyset(&sa.sa_mask);
  sigaction(SIGSEGV, &sa, NULL);

  pthread_create( &dummy1, NULL, sig_handler_2, (void *) &i1 );

  //sa.sa_handler = signal_handler_2;
  //sigaction(SIGSEGV, &sa, NULL);
  pthread_create( &dummy2, NULL, sig_handler_2, (void *) &i2 );

  //sa.sa_handler = signal_handler_3;
  //sigaction(SIGSEGV, &sa, NULL);
  pthread_create( &dummy3, NULL, sig_handler_2, (void *) &i3);
 
  for (;;)
    {
      /* whatever you want your program to
       * do... */
      /* grab the mutex before looking
       * at handled_signal */
     
      pthread_mutex_lock( &sig_mutex );
      /* look to see if any signals have
       * been caught */
      switch ( handled_signal )
      {
      case -1:
        /* no signal has been caught
         * by the signal handler */
        break;
      case 0:
        printf("The signal handler caught a signal I'm not interested in (%d)\n",handled_signal);
        handled_signal = -1;
        break;
      case SIGQUIT:
        printf("The signal handler caught a SIGQUIT signal!\n" );
        handled_signal = -1;
        break;
      case SIGINT:
        printf( "The signal handler caught a SIGINT signal!\n" );
        handled_signal = -1;
        break;
      }
      /* remember to release mutex */
      pthread_mutex_unlock(&sig_mutex);
    }
}




LVL 4
oumerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

mtmikeCommented:
Using threads and signals together is indeed a sure way to shoot yourself in the foot. Especially with the current linuxthreads implementation.

The problem is that you can only call async signal safe functions from a signal handler. Very few functions are async signal safe. In particular, writing to files, locking mutexes and canceling threads are not safe.

Another problem is that some signals are process global. Such a signal is never send to a particular thread, but always to the process as a whole. That is, any one thread that does not block the signal. Currently, the kernel always targets a specific thread. This is non-conforming and being worked on. Linuxthreads will be replaced very soon (with nptl or ngpt) and the upcoming 2.6 kernel contains a number threading enhancements.

The above sometimes causes threads to keep running, while the process should have exited.

I think the best way to perform cleanup on exit is by calling exit() in those signal handlers that normally kill the process and register the cleanup handler using atexit(). Save the signal number to some global variable in case you want to know what signal caused the exit. Don't do the cleanup in the signal handler.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
oumerAuthor Commented:
I think your solution works, but don't get it completely. Anyway, that will do for the moment. Thanks!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Linux OS Dev

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.