Solved

pthreads and signals

Posted on 2003-10-28
2
10,184 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
[X]
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
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

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Trying to install Node.js on Linux instance. Erroring.... 2 80
Problem to telnet 23 166
what is this linux system is? 16 121
Linux : how to point the OS to use the new DNS server 11 109
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…
How to Install VMware Tools in Red Hat Enterprise Linux 6.4 (RHEL 6.4) Step-by-Step Tutorial

733 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