If I setup and signal handler for SIGABRT and meanwhile I have a thread that waits on sigwait() for SIGABRT to come (I have a blocked SIGABRT in other threads by pthread_sigmask).
So which one will be processed first ? Signal handler or sigwait() ?
[I am facing some issues that sigwait() is get blocked for ever. I am debugging it currently]
main()
{
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigprocmask(SIG_BLOCK, &signal_set, NULL);
// Dont deliver SIGABORT while running this thread and it's kids.
pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
pthread_create(&tAbortWaitThread, NULL, WaitForAbortThread, NULL);
..
Create all other threads
...
}
static void* WaitForAbortThread(void* v)
{
sigset_t signal_set;
int stat;
int sig;
sigfillset( &signal_set);
pthread_sigmask( SIG_BLOCK, &signal_set, NULL ); // Dont want any signals
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT); // Add only SIGABRT
// This thread while executing , will handle the SIGABORT signal via signal handler.
pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
stat= sigwait( &signal_set, &sig ); // lets wait for signal handled in CatchAbort().
while (stat == -1)
{
stat= sigwait( &signal_set, &sig );
}
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
// Abort signal handler executed via sigaction().
static void CatchAbort(int i, siginfo_t* info, void* v)
{
sleep(20); // Dont return , hold on till the other threads are down.
}
Here at sigwait(), i will come to know that SIGABRT is received. I will tell other threads about it. Then will hold abort signal handler so that process is not terminated.
I wanted to know the interaction of sigwait() and the signal handler.
From sigwait() documentation :
The sigwait() function suspends execution of the calling thread until
one of the signals specified in the signal set becomes pending.
A pending signal means a blocked signal waiting to be delivered to one of the thread/process. Therefore, you need not to unblock the signal like you did with your pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL) call.
This should work :
static void* WaitForAbortThread(void* v){
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigwait( &signal_set, &sig );
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
I got some information from this <link>
It says :
To allow a thread to wait for asynchronously generated signals, the threads library provides the sigwait subroutine. The sigwait subroutine blocks the calling thread until one of the awaited signals is sent to the process or to the thread. There must not be a signal handler installed on the awaited signal using the sigwait subroutine.
I will remove the sigaction() handler and try only sigwait().
From the code snippet you've posted, it seems you got the use of sigwait() wrong. AFAIU, you need WaitForAbortThread like below:
sigemptyset( &signal_set); // change it from sigfillset()
for (;;) {
stat = sigwait(&signal_set, &sig);
if (sig == SIGABRT) {
printf("here's sigbart.. do whatever you want.\n");
pthread_kill(tid, signal); // thread id and signal
}
}
I don't think pthread_sigmask() is really needed. Since you only want to handle SIGABRT, first init signal_set as empty then simply add SIGABRT, then jump into the infinite loop, sigwait will wait for the particular signal that you're looking for, you check the signal if it's SIGABRT, if yes - do whatever you want. NOTE the uses of pthread_kill(), use it to sent any signal to other threads specified via tid and the signal you want to sent, make sure you know the tid of other threads you want to sent signal. Hope this will help!
I know this question is about a year old, but I often use a pattern, which solves exactly this issue using pthreads and signals. It is a little length but takes care of any issues I am aware of.
I recently used in combination with a library wrapped with SWIG and called from within Python. An annoying issue was that my IRQ thread waiting for SIGINT using sigwait never received the SIGINT signal. The same library worked perfectly when called from Matlab, which didn't capture the SIGINT signal.
The solution was to install a signal handler
#define _NTHREADS 8
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> // strerror
#define CallErr(fun, arg) { if ((fun arg)<0) \
FailErr(#fun) }
#define CallErrExit(fun, arg, ret) { if ((fun arg)<0) \
FailErrExit(#fun,ret) }
#define FailErrExit(msg,ret) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr); \
return ret; }
#define FailErr(msg) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr);}
typedef struct thread_arg {
int cpu_id;
int thread_id;
} thread_arg_t;
static jmp_buf jmp_env;
static struct sigaction act;
static struct sigaction oact;
size_t exitnow = 0;
pthread_mutex_t exit_mutex;
pthread_attr_t attr;
pthread_t pids[_NTHREADS];
pid_t tids[_NTHREADS+1];
static volatile int status[_NTHREADS]; // 0: suspended, 1: interrupted, 2: success
sigset_t mask;
static pid_t gettid( void );
static void *thread_function(void *arg);
static void signalHandler(int);
int main() {
cpu_set_t cpuset;
int nproc;
int i;
thread_arg_t thread_args[_NTHREADS];
int id;
CPU_ZERO( &cpuset );
CallErr(sched_getaffinity,
(gettid(), sizeof( cpu_set_t ), &cpuset));
nproc = CPU_COUNT(&cpuset);
for (i=0 ; i < _NTHREADS ; i++) {
thread_args[i].cpu_id = i % nproc;
thread_args[i].thread_id = i;
status[i] = 0;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_mutex_init(&exit_mutex, NULL);
// We pray for no locks on buffers and setbuf will work, if not we
// need to use filelock() on on FILE* access, tricky
setbuf(stdout, NULL);
setbuf(stderr, NULL);
act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT;
act.sa_handler = signalHandler;
sigemptyset(&act.sa_mask);
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
if (setjmp(jmp_env)) {
if (gettid()==tids[0]) {
// Main Thread
printf("main thread: waiting for clients to terminate\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
if (status[i] == 1)
printf("thread %d: terminated\n",i+1);
}
// On linux this can be done immediate after creation
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
else {
// Should never happen
printf("worker thread received signal");
}
return -1;
}
// Install handler
CallErr(sigaction, (SIGINT, &act, &oact));
// Block SIGINT
CallErr(pthread_sigmask, (SIG_BLOCK, &mask, NULL));
tids[0] = gettid();
srand ( time(NULL) );
for (i = 0; i < _NTHREADS; i++) {
// Inherits main threads signal handler, they are blocking
CallErr(pthread_create,
(&pids[i], &attr, thread_function,
(void *)&thread_args[i]));
}
if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL)) {
fprintf(stderr, "main thread: can't block SIGINT");
}
printf("Infinite loop started - CTRL-C to exit\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
//printf("%d\n",status[i]);
if (status[i] == 2)
printf("thread %d: finished succesfully\n",i+1);
}
// Clean up and exit
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
static void signalHandler(int sig) {
int i;
pthread_t id;
id = pthread_self();
for (i = 0; i < _NTHREADS; i++)
if (pids[i] == id) {
// Exits if worker thread
printf("Worker thread caught signal");
break;
}
if (sig==2) {
sigaction(SIGINT, &oact, &act);
}
pthread_mutex_lock(&exit_mutex);
if (!exitnow)
exitnow = 1;
pthread_mutex_unlock(&exit_mutex);
longjmp(jmp_env, 1);
}
void *thread_function(void *arg) {
cpu_set_t set;
thread_arg_t* threadarg;
int thread_id;
threadarg = (thread_arg_t*) arg;
thread_id = threadarg->thread_id+1;
tids[thread_id] = gettid();
CPU_ZERO( &set );
CPU_SET( threadarg->cpu_id, &set );
CallErrExit(sched_setaffinity, (gettid(), sizeof(cpu_set_t), &set ),
NULL);
int k = 8;
// While loop waiting for exit condition
while (k>0) {
sleep(rand() % 3);
pthread_mutex_lock(&exit_mutex);
if (exitnow) {
status[threadarg->thread_id] = 1;
pthread_mutex_unlock(&exit_mutex);
pthread_exit(NULL);
}
pthread_mutex_unlock(&exit_mutex);
k--;
}
status[threadarg->thread_id] = 2;
pthread_exit(NULL);
}
static pid_t gettid( void ) {
pid_t pid;
CallErr(pid = syscall, (__NR_gettid));
return pid;
}
I run serveral tests and the conbinations and results are:
For all test cases, I register a signal handler by calling sigaction in the main thread.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A sleep, send target signal.
result: signal handler is executed in thread A.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A calls sigwait, send target signal.
result: sigwait is executed.
main thread does not block target signal, thread A does not block target signal, thread A calls sigwait, send target signal.
result: main thread is chosen and the registered signal handler is executed in the main thread.
As you can see, conbination 1 and 2 are easy to understand and conclude.
It is:
If a signal is blocked by a thread, then the process-wide signal handler registered by sigaction just can't catch or even know it.
If a signal is not blocked, and it's sent before calling sigwait, the process-wide signal handler wins. And that's why APUE the books require us to block the target signal before calling sigwait. Here I use sleep in thread A to simulate a long "window time".
If a signal is not blocked, and it's sent when sigwait has already been waiting, sigwait wins.
But you should notice that for test case 1 and 2, main thread is designed to block the target signal.
At last for test case 3, when main thread is not blocked the target signal, and sigwait in thread A is also waiting, the signal handler is executed in the main thread.
I believe the behaviour of test case 3 is what APUE talks about:
From APUE ยง12.8:
If a signal is being caught (the process has established a signal
handler by using sigaction, for example) and a thread is waiting for
the same signal in a call to sigwait, it is left up to the
implementation to decide which way to deliver the signal. The
implementation could either allow sigwait to return or invoke the
signal handler, but not both.
Above all, if you want to accomplish one thread <-> one signal model, you should:
block all signals in the main thread with pthread_sigmask (subsequent thread created in main thread inheris the signal mask)
create threads and call sigwait(target_signal) with target signal.
test code
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
FILE* file;
void* threadA(void* argv){
fprintf(file, "%ld\n", pthread_self());
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGUSR1);
int signo;
int err;
// sigset_t q;
// sigemptyset(&q);
// pthread_sigmask(SIG_SETMASK, &q, NULL);
// sleep(50);
fprintf(file, "1\n");
err = sigwait(&m, &signo);
if (err != 0){
fprintf(file, "sigwait error\n");
exit(1);
}
switch (signo)
{
case SIGUSR1:
fprintf(file, "SIGUSR1 received\n");
break;
default:
fprintf(file, "?\n");
break;
}
fprintf(file, "2\n");
}
void hello(int signo){
fprintf(file, "%ld\n", pthread_self());
fprintf(file, "hello\n");
}
int main(){
file = fopen("daemon", "wb");
setbuf(file, NULL);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = hello;
sigaction(SIGUSR1, &sa, NULL);
sigset_t n;
sigemptyset(&n);
sigaddset(&n, SIGUSR1);
// pthread_sigmask(SIG_BLOCK, &n, NULL);
pthread_t pid;
int err;
err = pthread_create(&pid, NULL, threadA, NULL);
if(err != 0){
fprintf(file, "create thread error\n");
exit(1);
}
pause();
fprintf(file, "after pause\n");
fclose(file);
return 0;
}
run with ./a.out & (run in the background), and use kill -SIGUSR1 pid to test. Do not use raise. raise, sleep, pause are thread-wide.
Related
I'm a newbie in c development. Recently, I noticed a problem when I was learning multi-threaded development, when I set a signal in the main thread of Action and when I try to block the signal action set by the main thread in the child thread, I find that it does not work.
Here is a brief description of the code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
void *thread_start(void *_arg) {
sleep(2);
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR2);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
printf("child-thread executed\n");
while (true) {
sleep(1);
}
return NULL;
}
void sig_handler(int _sig) {
printf("executed\n");
}
int main(int argc, char *argv[]) {
pthread_t t_id;
int s = pthread_create(&t_id, NULL, thread_start, NULL);
if (s != 0) {
char *msg = strerror(s);
printf("%s\n", msg);
}
printf("main-thread executed, create [%lu]\n", t_id);
signal(SIGUSR2, sig_handler);
while (true) {
sleep(1);
}
return EXIT_SUCCESS;
}
The signal mask is a per-thread property, a thread will inherit whatever the parent has at time of thread creation but, after that, it controls its own copy.
In other words, blocking a signal in a thread only affects the delivery of signals for that thread, not for any other.
In any case, even if it were shared (it's not), you would have a potential race condition since you start the child thread before setting up the signal in the main thread. Hence it would be indeterminate as to whether the order was "parent sets up signal, then child blocks" or vice versa. But, as stated, that's irrelevant due to the thread-specific nature of the signal mask.
If you want a thread to control the signal mask of another thread, you will need to use some form of inter-thread communication to let the other thread do it itself.
As I wrote in a comment, any USR1 signal sent to the process will be delivered using the main thread. It's output will not tell you exactly what happened, so it is not really a good way to test threads and signal masks. Additionally, it uses printf() in a signal handler, which may or may not work: printf() is not an async-signal safe function, so it must not be used in a signal handler.
Here is a better example:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
/* This function writes a message directly to standard error,
without using the stderr stream. This is async-signal safe.
Returns 0 if success, errno error code if an error occurs.
errno is kept unchanged. */
static int write_stderr(const char *msg)
{
const char *end = msg;
const int saved_errno = errno;
int retval = 0;
ssize_t n;
/* If msg is non-NULL, find the string-terminating '\0'. */
if (msg)
while (*end)
end++;
/* Write the message to standard error. */
while (msg < end) {
n = write(STDERR_FILENO, msg, (size_t)(end - msg));
if (n > 0) {
msg += n;
} else
if (n != 0) {
/* Bug, should not occur */
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
/* Paranoid check that exactly the message was written */
if (!retval)
if (msg != end)
retval = EIO;
errno = saved_errno;
return retval;
}
static volatile sig_atomic_t done = 0;
pthread_t main_thread;
pthread_t other_thread;
static void signal_handler(int signum)
{
const pthread_t id = pthread_self();
const char *thread = (id == main_thread) ? "Main thread" :
(id == other_thread) ? "Other thread" : "Unknown thread";
const char *event = (signum == SIGHUP) ? "HUP" :
(signum == SIGUSR1) ? "USR1" :
(signum == SIGINT) ? "INT" :
(signum == SIGTERM) ? "TERM" : "Unknown signal";
if (signum == SIGTERM || signum == SIGINT)
done = 1;
write_stderr(thread);
write_stderr(": ");
write_stderr(event);
write_stderr(".\n");
}
static int install_handler(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = signal_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
}
void *other(void *unused __attribute__((unused)))
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
while (!done)
sleep(1);
return NULL;
}
int main(void)
{
pthread_attr_t attrs;
sigset_t mask;
int result;
main_thread = pthread_self();
other_thread = pthread_self(); /* Just to initialize it to a sane value */
/* Install HUP, USR1, INT, and TERM signal handlers. */
if (install_handler(SIGHUP) ||
install_handler(SIGUSR1) ||
install_handler(SIGINT) ||
install_handler(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Create the other thread. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
result = pthread_create(&other_thread, &attrs, other, NULL);
pthread_attr_destroy(&attrs);
if (result) {
fprintf(stderr, "Cannot create a thread: %s.\n", strerror(result));
return EXIT_FAILURE;
}
/* This thread blocks SIGUSR1. */
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
/* Ready to handle signals. */
printf("Send a HUP, USR1, or TERM signal to process %d.\n", (int)getpid());
fflush(stdout);
while (!done)
sleep(1);
pthread_join(other_thread, NULL);
return EXIT_SUCCESS;
}
Save it as e.g. example.c, and compile and run using
gcc -Wall -O2 example.c -pthread -o exprog
./exprog
It will block the USR1 signal in the main thread, and HUP and TERM in the other thread. It will also catch the INT signal (Ctrl+C), which is not blocked in either thread. When you send it the INT or TERM signal, the program will exit.
If you send the program the USR1 signal, you'll see that it will always be delivered using the other thread.
If you send the program a HUP signal, you'll see that it will always be delivered using the main thread.
If you send the program a TERM signal, it too will be delivered using the main thread, but it will also cause the program to exit (nicely).
If you send the program an INT signal, it will be delivered using one of the threads. It depends on several factors whether you'll always see it being delivered using the same thread or not, but at least in theory, it can be delivered using either thread. This signal too will cause the program to exit (nicely).
I have the following program where only one thread installs the signal handler. But when I tested the code by sending signal to each thread, all the threads execute the signal handler.
Does all the threads share the same signal handler. I was under the assumption that it would happen(threads share signal handler) only when the main process which spawns these threads install the signal handler.
And one more question is about the context in which the signal handler executes. Is it guaranteed that the signal sent to the particular thread will execute in the same thread context for the given scenario?
void handler(int signo, siginfo_t *info, void *extra)
{
printf("handler id %d and thread id %d\n",syscall( SYS_gettid ),pthread_self());
}
void signalHandler()
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigaction(SIGSEGV, &sa, NULL);
//sigaction(SIGINT, &sa, NULL);
}
void *threadfn0(void *p)
{
signalHandler();
printf("thread0\n");
while ( 1 )
{
pause();
}
}
void *threadfn1(void *p)
{
while(1){
printf("thread1\n");
sleep(15);
}
return 0;
}
void *threadfn2(void *p)
{
while(1){
printf("thread2\n");
sleep(15);
}
return 0;
}
int main()
{
pthread_t t0,t1,t2;
pthread_create(&t0,NULL,threadfn0,NULL);
printf("T0 is %d\n",t0);
pthread_create(&t1,NULL,threadfn1,NULL);
printf("T1 is %d\n",t1);
pthread_create(&t2,NULL,threadfn2,NULL);
printf("T2 is %d\n",t2);
sleep(10);
pthread_kill(t2,SIGSEGV);
sleep(10);
pthread_kill(t1,SIGSEGV);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t0,NULL);
return 0;
}
output:
T0 is 1110239552
T1 is 1088309568
T2 is 1120729408
thread0
thread1
thread2
handler id 18878 and thread id 1120729408
thread2
thread1
handler id 18877 and thread id 1088309568
thread1
From the manpage for signal(7):
The signal disposition is a per-process attribute: in a multithreaded application, the disposition of a particular signal is the same for all threads.
So all threads share the same handlers, yes. If you use pthread_kill() to send a signal to a specific thread, that thread should execute the handler (Depending on the thread's signal mask set with pthread_sigmask(), of course).
Also note that you can't safely use printf() or other stdio functions in a signal handler. See the list of allowed functions in signal-safety(7).
All threads share the signal handler. You can use pthread_sigmask() to select, which threads have which signals blocked or unblocked and therefore can execute the handler. If several threads have the same signal unblocked, then any of them can execute the handler.
So cleaned up and fixed example looks something like this:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/syscall.h>
static void handler (int signo, siginfo_t *info, void *extra)
{
printf ("SIGNAL %u, handler id %lu and thread id %lu\n", signo, syscall (SYS_gettid), pthread_self ());
}
static void signalHandler (void)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigaction (SIGSEGV, &sa, NULL);
}
static void *threadfn0 (void *p)
{
signalHandler ();
sigset_t set;
sigfillset (&set);
pthread_sigmask (SIG_UNBLOCK, &set, NULL);
printf ("thread0\n");
while (1) {
pause ();
}
}
static void *threadfn1 (void *p)
{
while (1) {
printf ("thread1\n");
sleep (15);
}
return 0;
}
static void *threadfn2 (void *p)
{
while (1) {
printf ("thread2\n");
sleep (15);
}
return 0;
}
int main (int argc, char *argv[])
{
pthread_t t0, t1, t2;
// By default, block all signals in all threads and
// unblock them only in one thread after signal handler
// is set up, to avoid race conditions
sigset_t set;
sigfillset (&set);
pthread_sigmask (SIG_BLOCK, &set, NULL);
pthread_create (&t0, NULL, threadfn0, NULL);
printf ("T0 is %lu\n", t0);
pthread_create (&t1, NULL, threadfn1, NULL);
printf ("T1 is %lu\n", t1);
pthread_create (&t2, NULL, threadfn2, NULL);
printf ("T2 is %lu\n", t2);
pthread_kill (t2, SIGSEGV);
pthread_kill (t1, SIGSEGV);
pthread_kill (t0, SIGSEGV);
pthread_join (t2, NULL);
pthread_join (t1, NULL);
pthread_join (t0, NULL);
return 0;
}
I written the below code to handle signals in separate thread to forcefully cleanup some resources and exit the complete process.
Here is the brief note about the below code.
When the signal is received, set volatile sig_atomic_t sig_set_flag = 1; inside signal handler.
In signal_handler_thread, checking sig_set_flag value in a loop.
if(sig_set_flag==1) send notifications like "i am going down" from signal_handler_thread and call exit(0); from the thread.
Signals can be received by any thread in a process. So i am setting the global variable.
I have 2 questions.
1) This implementation is fine? or i have to block the signals for the main thread and handle only by the spawned thread ?
2) How to block a signal to the main process and handle it in a thread?
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
/*
* Set this variable if any signal is received
*/
volatile sig_atomic_t sig_set_flag = 0;
pthread_mutex_t cleanup_mutex;
/*
* Resource cleanup function.
*/
int cleaup_resources() {
pthread_mutex_lock(&cleanup_mutex);
/*
* Send notification to all the clients.
* Delete all the temp files
*/
printf("Notified to clients.Exiting process\n");
pthread_mutex_unlock(&cleanup_mutex);
return 0;
}
/*
* Signal handler thread
*/
void sig_term_handler(int sig_num) {
sig_set_flag = sig_num;
}
/*
* Signal handler thread routine
*/
void *signal_handler_thread(void * args) {
while(1) {
if(sig_set_flag != 0) {
printf("%s : Signal flag is set for sig_no %d\n",__func__,sig_set_flag);
cleaup_resources();
break;
}
usleep(5);
}
exit(0);
}
int main()
{
int loop_count,status;
pthread_t tid;
pid_t pid;
struct sigaction sig;
sig.sa_handler = &sig_term_handler;
sig.sa_flags = 0;
sigaction(SIGTERM, &sig, NULL);
/*
* Spawn a thread to monitor signals.
* If signal received, Exit the process.
*/
pthread_create(&tid, NULL, signal_handler_thread, NULL);
while(1) {
printf("Some time consuming task in progress... PID = %d\n",getpid());
pid = fork();
if(pid == 0) {
sleep(100);
return 0;
} else {
waitpid(pid, &status, 0);
loop_count++;
if( loop_count>=10)
break;
}
}
cleaup_resources();
exit(0);
}
Note:I know signals will interrupt the some system calls and EINTR will be set. Unfortunately some system calls (i.e) waitpid() will not be interrupted. So i spawned a thread to handle this scenario.
1) Your implementation seems to be correct. signal() and sigaction() register a handler function for the whole process, so it doesn't matter you call them in the main thread or in the spawned thread.
2) To block a signal in the main thread, and handle it in a thread, you have to design, not a handler function, but a handler thread, using sigwait() or sigwaitinfo(). So the thread will wait for the signals and the program execution won't be interrupted.
In this case, you have to block process-wide signals in all the threads, including the main thread. If it is not blocked, the signal will have the default behavior on the program.
You have to use pthread_sigmask() to block one or more signals. An example of code to block SIGTERM:
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGTERM);
pthread_sigmask(SIG_BLOCK,&set,NULL);
When a thread is created, it inherits of the blocked signals of the creator thread.
I modified your code to show you how to use sigwaitinfo() and pthread_sigmask():
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
pthread_mutex_t cleanup_mutex;
/*
* Resource cleanup function.
*/
int cleaup_resources() {
pthread_mutex_lock(&cleanup_mutex);
/*
* Send notification to all the clients.
* Delete all the temp files
*/
printf("Notified to clients.Exiting process\n");
pthread_mutex_unlock(&cleanup_mutex);
return 0;
}
/*
* Signal handler thread routine
*/
void *signal_handler_thread(void * args) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
siginfo_t info;
while(1) {
sigwaitinfo(&set,&info);
if(info.si_signo == SIGINT){
printf("\nSIGINT received\n");
cleaup_resources();
exit(0);
}
}
}
int main()
{
int loop_count,status;
pthread_t tid;
pid_t pid;
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGINT);
pthread_sigmask(SIG_BLOCK,&set,NULL);
// The new thread will inherit the blocked
// signals from the thread that create it:
pthread_create(&tid, NULL, signal_handler_thread, NULL);
while(1) {
printf("Some time consuming task in progress... PID = %d\n",getpid());
pid = fork();
if(pid == 0) {
sleep(100);
return 0;
} else {
waitpid(pid, &status, 0);
loop_count++;
if( loop_count>=10)
break;
}
}
cleaup_resources();
exit(0);
}
Also, be careful of the fork(), from the tests I have done, the child process will inherit of the blocked signals.
I am writing a program to demonstrate signal handling in a secondary thread. In my program, main thread spawns 10 thread and each thread calls sigwait to wait for signal. But in my case, it is main thread which is handling signa. Code is given below:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <errno.h>
volatile sig_atomic_t cont = 1;
volatile sig_atomic_t wsig = 0;
volatile sig_atomic_t wtid = 0;
int GetCurrentThreadId()
{
return syscall(__NR_gettid);
}
void Segv1(int, siginfo_t *, void *)
{
//printf("SIGSEGV signal on illegal memory access handled by thread: %d\n", GetCurrentThreadId());
wtid = GetCurrentThreadId();
wsig = SIGSEGV;
_exit(SIGSEGV);
}
void Fpe1(int , siginfo_t *, void *)
{
wtid = GetCurrentThreadId();
wsig = SIGFPE;
_exit(SIGFPE);
}
void User1(int, siginfo_t *, void *)
{
wtid = GetCurrentThreadId();
wsig = SIGUSR1;
}
void* ThreadFunc (void*)
{
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGUSR1);
sigaddset(&sigs, SIGSEGV);
sigaddset(&sigs, SIGFPE);
pthread_sigmask(SIG_BLOCK, &sigs, NULL);
//printf("Thread: %d starts\n", GetCurrentThreadId());
while(cont) {
//printf("Thread: %d enters into loop\n", GetCurrentThreadId());
//int s = sigwaitinfo(&sigs, NULL);
//int sig;
//int s = sigwait(&sigs, &sig);
//printf("A signal\n");
/*if(s==0) {
sigaddset(&sigs, sig);
printf("Signal %d handled from thread: %d\n", sig, GetCurrentThreadId());
if(sig==SIGFPE||sig==SIGSEGV)
return NULL;
} else {
printf("sigwaitinfo failed with %d\n", s);
break;
}*/
int s = sigsuspend(&sigs);
switch(wsig) {
case SIGSEGV:
printf("Segmenation fault in thread: %d Current thread id: %d\n", wtid, GetCurrentThreadId());
exit(1);
break;
case SIGFPE:
printf("Floating point exception in thread: %d Current thread id: %d\n", wtid, GetCurrentThreadId());
exit(1);
break;
case SIGUSR1:
printf("User 1 signal in thread: %d Current thread id: %d\n", wtid, GetCurrentThreadId());
break;
default:
printf("Unhandled signal: %d in thread: %d Current thread id: %d\n", wsig, wtid, GetCurrentThreadId());
break;
}
}
printf("Thread: %d ends\n", GetCurrentThreadId());
return NULL;
}
int main()
{
printf("My PID: %d\n", getpid());
printf("SIGSEGV: %d\nSIGFPE: %d\nSIGUSR1: %d\n", SIGSEGV, SIGFPE, SIGUSR1);
//Create a thread for signal
struct sigaction act;
memset(&act, 0, sizeof act);
act.sa_sigaction = User1;
act.sa_flags = SA_SIGINFO;
//Set Handler for SIGUSR1 signal.
if(sigaction(SIGUSR1, &act, NULL)<0) {
fprintf(stderr, "sigaction failed\n");
return 1;
}
//Set handler for SIGSEGV signal.
act.sa_sigaction = Segv1;
sigaction(SIGSEGV, &act, NULL);
//Set handler for SIGFPE (floating point exception) signal.
act.sa_sigaction = Fpe1;
sigaction(SIGFPE, &act, NULL);
sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset, SIGUSR1);
sigaddset(&sset, SIGSEGV);
sigaddset(&sset, SIGFPE);
//pthread_sigmask(SIG_BLOCK, &sset, NULL);
const int numthreads = 10;
pthread_t tid[numthreads];
for(int i=0;i<numthreads;++i)
pthread_create(&tid[i], NULL, ThreadFunc, NULL);
sleep(numthreads/2);
int sleepval = 15;
int pid = fork();
if(pid) {
while(sleepval) {
sleepval = sleep(sleepval);
//It might get interrupted with signal.
switch(wsig) {
case SIGSEGV:
printf("Segmenation fault in thread: %d\n", wtid);
exit(1);
break;
case SIGFPE:
printf("Floating point exception in thread: %d\n", wtid);
exit(1);
break;
case SIGUSR1:
printf("User 1 signal in thread: %d\n", wtid);
break;
default:
printf("Unhandled signal: %d in thread: %d\n", wsig, wtid);
break;
}
}
} else {
for(int i=0;i<10;++i) {
kill(getppid(), SIGUSR1);
//If sleep is not used, signal SIGUSR1 will be handled one time in parent
//as other signals will be ignored while SIGUSR1 is being handled.
sleep(1);
}
return 0;
}
int * a = 0;
//*a = 1;
int c=0;
//c = 0;
int b = 1/c; //send SIGFPE signal.
return 0;
}
Is there any rule of picking up the thread for signal handling on Linux and Mac OS X? What should I do so that signal got handled in secondary thread?
In the above program, I am not able to handle the signal in secondary thread. What is wrong with it?
I suggest you should SIG_BLOCK needed signals in main thread (commented out now) and SIG_UNBLOCK them in other threads (SIG_BLOCK now). Or you can spawn you threads and after it SIG_BLOCK in main thread, as spawned threads got their sigmask from parent.
And sigsuspend's parameter is not the signals you want to wake up on, but vice versa.
Is there any rule of picking up the thread for signal handling on Linux and Mac OS X?
There is, see Signal Generation and Delivery:
During the time between the generation of a signal and its delivery or acceptance, the signal is said to be pending. Ordinarily, this interval cannot be detected by an application. However, a signal can be blocked from delivery to a thread. If the action associated with a blocked signal is anything other than to ignore the signal, and if that signal is generated for the thread, the signal shall remain pending until it is unblocked, it is accepted when it is selected and returned by a call to the sigwait() function, or the action associated with it is set to ignore the signal. Signals generated for the process shall be delivered to exactly one of those threads within the process which is in a call to a sigwait() function selecting that signal or has not blocked delivery of the signal. If there are no threads in a call to a sigwait() function selecting that signal, and if all threads within the process block delivery of the signal, the signal shall remain pending on the process until a thread calls a sigwait() function selecting that signal, a thread unblocks delivery of the signal, or the action associated with the signal is set to ignore the signal. If the action associated with a blocked signal is to ignore the signal and if that signal is generated for the process, it is unspecified whether the signal is discarded immediately upon generation or remains pending.
I would like to use SIGUSR1 and SIGUSR2 as arguments to pthread_kill() to suspend the execution of the running thread(i.e thread sends signal to itself) and resuming a suspended thread by a peer thread when a condition is met.
Would be gratefull for any pointers like example code or views about it.
Thanks
Signalling is used as the following example. However, in your case though, you need Condition variables
https://computing.llnl.gov/tutorials/pthreads/#ConVarSignal has an example
pthread_cond_wait (condition,mutex)
pthread_cond_signal (condition)
pthread_cond_broadcast (condition)
Example with the correct usage of the signals with pthread_kill() is demonstrated below
/* ptsig.c
This program illustrates the use of signals in threads.
Three threads including the main thread.
main thread
a. Set up a signal mask to block all signals.
b. Set up signal handlers for SIGINT and SIGUSR1.
c. Create thread_1, detached.
d. Create thread_2, nondetached.
e. Send SIGINT & SIGUSR1 to thread_1.
f. Quit.
thread_1
a. Unblock all to embrace all signals.
b. Wait for signals.
c. Send SIGINT and SIGUSR1 to thread_2
d. Wait for thread_2 to terminate
e. Print thread_2 return status.
f. Quit
thread_2
a. Unblock SIGUSR1 -- all others blocked due to inheritance.
b. Wait for signals.
c. Quit
Note: There is hardly any error checking in this example -- not a good
idea, but to make the program a bit more easier to explain.
To compile: gcc ptsig.c -lpthread
Sam Hsu (11/19/10)
*/
#define _POSIX_C_SOURCE 199506L
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
pthread_t tid2;
static void int_handler(int signo), usr1_handler(int signo);
void millisleep(int milliseconds)
{
usleep(milliseconds * 1000);
}
main()
{
pthread_t tid1;
pthread_attr_t attr_obj; /* a thread attribute variable */
void *thread_1(void *), *thread_2(void *);
sigset_t sigmask;
struct sigaction action;
/* set up signal mask to block all in main thread */
sigfillset(&sigmask); /* to turn on all bits */
pthread_sigmask(SIG_BLOCK, &sigmask, (sigset_t *)0);
/* set up signal handlers for SIGINT & SIGUSR1 */
action.sa_flags = 0;
action.sa_handler = int_handler;
sigaction(SIGINT, &action, (struct sigaction *)0);
action.sa_handler = usr1_handler;
sigaction(SIGUSR1, &action, (struct sigaction *)0);
pthread_attr_init(&attr_obj); /* init it to default */
pthread_attr_setdetachstate(&attr_obj, PTHREAD_CREATE_DETACHED);
pthread_create(&tid1, &attr_obj, thread_1, (void *)NULL);
printf("TID(%u) created\n", (unsigned int)tid1);
pthread_attr_setdetachstate(&attr_obj, PTHREAD_CREATE_JOINABLE);
pthread_create(&tid2, &attr_obj, thread_2, (void *)NULL);
printf("TID(%u) created\n", (unsigned int)tid2);
millisleep(1000); /* for some reason a sleep is needed here */
printf("main(%u) sending SIGINT to TID(%u)\n", (unsigned int)pthread_self(), (unsigned int)tid1);
pthread_kill(tid1, SIGINT); /* not blocked by tid1 */
printf("main(%u) sending SIGUSR1 to TID(%u)\n", (unsigned int)pthread_self(), (unsigned int)tid1);
pthread_kill(tid1, SIGUSR1); /* not blocked by tid1 */
printf("main(%u) is terminating\n", (unsigned int)pthread_self());
pthread_exit((void *)NULL); /* will not terminate process */
} /* main */
void *thread_1(void *dummy)
{
int sig, status, *status_ptr = &status;
sigset_t sigmask;
sigfillset(&sigmask); /* will unblock all signals */
pthread_sigmask(SIG_UNBLOCK, &sigmask, (sigset_t *)0);
sigwait(&sigmask, &sig);
switch(sig) {
case SIGINT:
int_handler(sig);
break;
default:
break;
}
printf("TID(%u) sending SIGINT to %u\n", (unsigned int)pthread_self(), (unsigned int)tid2);
pthread_kill(tid2, SIGINT); /* blocked by tid2 */
printf("TID(%u) sending SIGUSR1 to %u\n", (unsigned int)pthread_self(), (unsigned int)tid2);
pthread_kill(tid2, SIGUSR1); /* not blocked by tid2 */
pthread_join(tid2, (void **)status_ptr);
printf("TID(%u) exit status = %d\n", (unsigned int)tid2, status);
printf("TID(%u) is terminating\n", (unsigned int)pthread_self());
pthread_exit((void *)NULL); /* calling thread will terminate */
} /* thread_1 */
void *thread_2(void *dummy)
{
int sig;
sigset_t sigmask;
sigemptyset(&sigmask); /* to zero out all bits */
sigaddset(&sigmask, SIGUSR1); /* to unblock SIGUSR1 */
pthread_sigmask(SIG_UNBLOCK, &sigmask, (sigset_t *)0);
sigwait(&sigmask, &sig);
switch(sig) {
case SIGUSR1:
usr1_handler(sig);
break;
default:
break;
}
printf("TID(%u) is terminating\n", (unsigned int)pthread_self());
pthread_exit((void *)NULL); /* calling thread will terminate */
} /* thread_2 */
static void int_handler(int dummy)
{
printf("SIGINT received by TID(%u)\n", (unsigned int)pthread_self());
} /* int_handler */
static void usr1_handler(int dummy)
{
printf("SIGUSR1 received by TID(%u)\n", (unsigned int)pthread_self());
} /* usr1_handler */
pthread_cancel() is safer relative to pthread_kill()
An example of pthread_cancel() below
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static void *
thread_func(void *ignored_argument)
{
int s;
s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (s != 0)
handle_error_en(s, "pthread_setcancelstate");
while (1) {
printf("sleeping\n");
sleep(1); /* Should get canceled while we sleep */
}
/* sleep() is a cancellation point */
/* Should never get here */
printf("thread_func(): not canceled!\n");
return NULL;
}
int
main(void)
{
pthread_t thr;
void *res;
int s;
/* Start a thread and then send it a cancellation request */
s = pthread_create(&thr, NULL, &thread_func, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
sleep(10); /* Give thread a chance to get started */
printf("main(): sending cancellation request\n");
s = pthread_cancel(thr);
if (s != 0)
handle_error_en(s, "pthread_cancel");
/* Join with thread to see what its exit status was */
s = pthread_join(thr, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("main(): thread was canceled\n");
else
printf("main(): thread wasn't canceled (shouldn't happen!)\n");
exit(EXIT_SUCCESS);
}
While I agree with Nemo, there are some valid use cases for suspending threads via signals. The susp.c code in PWPT is certainly a good base but you can also find it used in GLIBC. You might also wish to break the thread out of a blocking-IO system call (see this question) or interrupt a semaphore wait (see this question).