Reputation: 3335
I have a server socket thread that accepts connections. It uses select with timeout 1 second in order to do other things. However sometimes I want to do these things right away when a specific signal comes to the application from outside. In a single thread program a signal interrupts sleep and timed IO blocking functions like select. However in a thread other than main signals do not interrupt sleep.
I can signal from main thread using pthread_kill() and the sleep is interrupted, but when the signal comes from outside it doesn't interrupt the sleep in thread. If I install a handler and inside the handler I call pthread_kill() to that thread the handler catches the signal and enters infinite loop.
How do I interrupt the sleep by a signal from outside the program - for example SIGUSR1.
I could do this workaround: I could start another thread to do the other things I do in the server thread. It would wait on a condition and then the main thread would signal the waiting thread on a signal. But I want to really want to know to interrupt sleep/select in other threads - for example for quicker shutdown I want to interrupt all waiting and let the threads quit a bit faster. Also I want to understand how signals work in other threads. And sometimes you want to interrupt a sleep that's not in your code (another library)
I read about pthread_sigmask(), but I'm not sure I understand it. In main thread handlers are installed to handle signals. What happens in other threads? This example uses sigwait() to receive signal. What happens if you don't call sigwait? Are signals accumulated in a queue or just they don't have an effect on a thread that doesn't wait for them?
Upvotes: 0
Views: 447
Reputation: 3335
Here is what I came up with. I don't know if there is another solution. I'll wait for suggestions.
To signal that work needs to be done SIGUSR1 is sent to the program. It sends SIGALERT to the threads to make them interrupt blocking code like sleep, select, etc. As a side effect blocking in main thread also gets interrupted, even though the signals are sent to the other threads only.
It can't be done with just one signal - like handle sigalert and then signal alert the two other threads using pthread_kill, because for it enters an endless loop.
#include <stdio.h>
#include <stdlib.h> // EXIT_FAILURE
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h> // for errno
#include <pthread.h>
#ifndef __cplusplus
#define true 1
#define false 0
#define nullptr NULL
typedef int bool;
#endif
// I copied this. I don't know why it's in a loop
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
pthread_t t1;
pthread_t t2;
pthread_t st;
pthread_t mainThreadId; // main thread
void *workerThread(void * param);
void unblockSignals();
void alertSignaHlandler(int sigNum);
void installSignalHandler(int sigNum, __sighandler_t handler);
void sigUsr1Handler(int sigNum) {
pthread_kill(t1, SIGALRM);
// pthread_kill(t2, SIGALRM); // let's say we only want t1 to interrupt
}
// on SIGTERM we want all threads to interrupt sleep so they can quit faster
void sigTermHandler(int sigNum) {
// setRunning(false); // also implement a global running vairable with a mutex, that other threads check to quit
pthread_kill(t1, SIGALRM);
pthread_kill(t2, SIGALRM);
// pthread_kill(mainThreadId, SIGALRM); // main thread will be interrupted by the signal anyway.
}
void alertSignaHlandler(int sigNum) {}
int main() {
void * ret1;
void * ret2;
mainThreadId = pthread_self();
printf("main starting with pid %d\n", getpid());
installSignalHandler(SIGALRM, alertSignaHlandler); // we need this to prevent program termination on alarm
installSignalHandler(SIGUSR1, sigUsr1Handler); // send this signal to interrupt other threads
installSignalHandler(SIGTERM, sigTermHandler);
installSignalHandler(SIGINT, sigTermHandler);
pthread_create(&t1, NULL, &workerThread, (void *)1);
pthread_create(&t2, NULL, &workerThread, (void *)2);
// simulate work in main thread. Only interrupt this on SIGTERM or SIGINT (ctrl-c)
time_t ts = time(NULL);
sleep(20);
ts = time(NULL) - ts;
printf("main(): sleep took %lds\n", ts);
pthread_join(t1, &ret1);
pthread_join(t2, &ret2);
printf("main thread reached end\n");
return 0;
}
void *workerThread(void * param){
long id = (long) param;
printf("workerThread %d starting...\n", id);
// in the real world this will be in a loop. somethign like this
// while(running) {
// // do something every minute
// doSomething();
// sleep(60); // this sleep we want to interrupt, because we don't want to wait 60 seconds to exit
// }
time_t ts = time(NULL);
sleep(60); // this can also be select, read or other timed blocking call.
ts = time(NULL) - ts;
printf("workerThread(%ld): sleep took %lds\n", id, ts);
return NULL;
}
void installSignalHandler(int sigNum, __sighandler_t handler){
struct sigaction newAction, oldAction;
memset(&newAction, '\0', sizeof(newAction));
newAction.sa_flags = 0;
//newAction.sa_flags = SA_RESETHAND;
newAction.sa_handler = handler;
sigaction(sigNum, &newAction, &oldAction);
}
Upvotes: 0