Solved

pthreads and signals

Posted on 2003-10-28
2
10,113 Views
Last Modified: 2012-06-27
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);
    }
}




0
Comment
Question by:oumer
2 Comments
 
LVL 5

Accepted Solution

by:
mtmike earned 250 total points
ID: 9638383
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
 
LVL 4

Author Comment

by:oumer
ID: 9667092
I think your solution works, but don't get it completely. Anyway, that will do for the moment. Thanks!
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Have you ever been frustrated by having to click seven times in order to retrieve a small bit of information from the web, always the same seven clicks, scrolling down and down until you reach your target? When you know the benefits of the command l…
The purpose of this article is to demonstrate how we can upgrade Python from version 2.7.6 to Python 2.7.10 on the Linux Mint operating system. I am using an Oracle Virtual Box where I have installed Linux Mint operating system version 17.2. Once yo…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now