29

I have created a pthread, and installed a signal handler inside that, same way as we do in main( ) function. The thread's signal handler is a separate function. Surprisingly, it is not working, that is the thread's signal handler is not able to catch signals.

Here is the code:

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 printf("Caught signal: %d\n",sig);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 printf("This is from thread function\n");
 signal(SIGSEGV,sig_func); // Register signal handler inside thread
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data *ptr;

 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 printf("Name:%s\n",ptr->name);
 printf("Age:%d\n",ptr->age);
}

Output:

Segmentation fault (which means the signal is not caught by handler)

Paul Floyd
  • 5,530
  • 5
  • 29
  • 43
RajSanpui
  • 11,556
  • 32
  • 79
  • 146
  • 7
    You've discovered that [threads and signals don't interact well](http://stackoverflow.com/questions/2575106/posix-threads-and-signals) :) – sarnold Mar 12 '11 at 11:09
  • 2
    To begin with, and to continue what @sarnold said, you're using the wrong API. Do not use `signal()`. From the man page (*read it*): "The effects of signal() in a multithreaded process are unspecified." Start reading docs at man 2 sigaction. – rlibby Mar 12 '11 at 11:16
  • @rlibby: So shall i use the "struct sigaction" or "sigevent structure" to catch the signals, you mean? – RajSanpui Mar 12 '11 at 11:19

3 Answers3

29

There are several problems with your code:

  • ptr is not initialised, so all the ptr-> parts will crash the program
  • you are calling pthread_kill() immediately, very likely before the signal handler has been installed, and in a thread (which has unspecified behaviour)
  • you call printf() from a signal handler, which is not guaranteed to work (see man 7 signal for a list of safe functions)

This will work a lot better, though you'd still need proper thread synchronisation, and as stated elsewhere, you should use sigaction():

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 write(1, "Caught signal 11\n", 17);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 fprintf(stderr, "This is from thread function\n");
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data d;
 data *ptr = &d;

 signal(SIGSEGV,sig_func); // Register signal handler before going multithread
 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 sleep(1); // Leave time for initialisation
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 fprintf(stderr, "Name:%s\n",ptr->name);
 fprintf(stderr, "Age:%d\n",ptr->age);
}

Edit: install sighandler in main thread

Paul Floyd
  • 5,530
  • 5
  • 29
  • 43
sam hocevar
  • 11,853
  • 5
  • 49
  • 68
  • Thanks a lot, it worked. So do u think only the pointer initialization was a problem? But if i don't deliver any signals the code works without errors. – RajSanpui Mar 12 '11 at 11:30
  • 1
    Also registering the signal handler at the main, instead of thread function works too. Why is it so? – RajSanpui Mar 12 '11 at 11:31
  • 1
    @kingsmasher: the two main problems were the pointer initialisation and the fact that you called `pthread_kill()` before the child thread had time to set the signal handler. Without these two fixes, there was no way your program could run as expected. The other problems are portability issues that will cause unexpected behaviour on various Linux or Unix flavours so should be addressed, too. Until then, consider that it only works by chance. – sam hocevar Mar 12 '11 at 11:40
  • 2
    Also worth noting: my man page for `signal()` says "The effects of `signal()` in a multithreaded process are unspecified." And it recommends using `sigaction()` as well. – Julien-L Apr 09 '13 at 23:22
  • *`ptr` is not initialised, so all the `ptr->` parts will crash the program* absolutely false, it really depends on a lot of things but it will certainly cause **undefined behavior**. – Iharob Al Asimi Sep 02 '15 at 16:02
10

I believe the core of the problem is that signals are delivered to the process as a whole, rather than individual threads. Commonly, a single thread is nominated to handle all signals; all other threads (including the main thread) need to block the signals using pthread_sigmask().

You can set the mask to block all signals, start your signal-handler-thread, unmask the signals you wish to handle, and then back in the main thread, start all the other threads you need. They will inherit the "block all signals" mask from the main thread.

Incidentally, it's time to move away from signal(3) and switch to sigaction(2), which has reliable semantics and is better standardized. (And thus more portable.)

sarnold
  • 102,305
  • 22
  • 181
  • 238
  • @sarnold: You said: "believe the core of the problem is that signals are delivered to the process as a whole, rather than individual threads" but if i use pthread_kill(thread_id,signal_no) the signal i think is delivered to he specific thread. – RajSanpui Mar 12 '11 at 11:23
  • @Kingsmasher1, I'm not sure that's true :) but @Sam Hocevar's answer looks awesome. I'm tempted to delete my answer, but it might yet be useful. – sarnold Mar 12 '11 at 11:26
  • @sarnold: signals are delivered to the process as a whole, but according to the `pthread_kill()` documentation, it ensures that the expected thread will receive the signal if the signal is not `SIGSTOP`, `SIGCONT` or `SIGTERM`. – sam hocevar Mar 12 '11 at 11:32
  • @Sam Hocevar, all the docs that claimed that came from the POSIX manpages, which are wonderful, but I don't trust them to report the reality of Linux threading APIs. :( If you've got docs from the NPTL people that say the same thing, that'd really brighten my day. :) – sarnold Mar 12 '11 at 11:35
  • NPTL claims to conform to POSIX, and it does a rather good job of it. I haven't found any glaring non-conformance issues except perhaps some things related to the kernel's insistence on having separate per-thread real/effective/saved/fs uid/gid and groups, and forcing the userspace to synchronize changing them all. (NPTL does not synchronize changes to the supplementary groups despite POSIX stating that the groups are a property of the process not the thread.) – R.. GitHub STOP HELPING ICE Mar 12 '11 at 13:36
6

The one problem with you code that nobody has mentioned yet is that, while signal blocking (and delivery, if you use pthread_kill or raise) are per-thread, signal handlers are per-process. This means they're a very bad mechanism for inter-thread communication, especially if your code will ever be used as library code, since it's extremely bad behavior for a library to alter the caller's signal handlers.

Also note that using signal handlers for communication between threads has sub-optimal performance compared to other methods of thread signaling like condition variables or barriers, because there's at least one additional user-kernel-user transition (when the signal handler returns).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Thank you for the careful investigation and your comments. You said "while signal blocking (and delivery, if you use pthread_kill or raise) are per-thread, signal handlers are per-process. " if we register the signal handler inside the thread, don't you think that it is with reference to only that thread? What is the difference between adding signal handlers (to be precise, register signal handlers) inside the main function, and inside threads? I know if we, register inside main( ) it takes care of threads too, since threads share the main( ) code. – RajSanpui Mar 13 '11 at 10:34
  • So if we register the handler inside the thread function, is it only specific to that thread? – RajSanpui Mar 13 '11 at 10:38
  • @kingsmahser1: no, nothing indicates that the storage for `signal` is or should be thread-local. You should assume that registering a signal handler has the same effect whatever the current thread is. – sam hocevar Mar 13 '11 at 20:31
  • 1
    As Sam said, signal handlers are **process global**. You cannot install per-thread signal handlers. You could try to be clever and install a signal handler that reads a function pointer with `pthread_getspecific` as the thread-specific handler for the signal, except that `pthread_getspecific` is not async-signal-safe. As far as I know, there's no async-signal-safe way to determine what thread you're in, so the only point of delivering a signal to a specific thread is generating `EINTR` (if `SA_RESTART` is omitted from `sigaction` flags) or stalling that thread's progress... – R.. GitHub STOP HELPING ICE Mar 14 '11 at 02:19