Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

pthreads and signals

Posted on 2003-10-28
2
10,166 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

Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

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 fix the unknown display problem in Linux Mint operating system. After installing the OS if you see Display monitor is not recognized then we can install "MESA" utilities to fix this problem or we can install additio…
Established in 1997, Technology Architects has become one of the most reputable technology solutions companies in the country. TA have been providing businesses with cost effective state-of-the-art solutions and unparalleled service that is designed…

856 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