I am testing a signal handler for my OS Class project. Basically my signal handler (which is running in it's own thread) have to handle SIGINT, this means that it has to "kill" all the other threads and then exit.
unfortunately my code does not work.
This is my dummy thread task, which I want to stop with a SIGINT
static void * dummyTask(){
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
while(1){
pthread_testcancel();
printf(" Hello!\n");
sleep(2);
}
return NULL;
}
Here is my main. As you can see I create the signal handler and then up to 2 dummy threads. I save their thID in an array, I need this later in the task of the signal handler thread, as you will see
int main(){
//need to the signal handler thread, so it can kill all the threads with cancel
pthread_t **thids = malloc( 2 * sizeof(pthread_t *));
sigset_t set;
pthread_t signalHandlerThread;
sigemptyset(&set);
sigaddset(&set, SIGINT);
//s is for error checking, not importanto now
int s = pthread_sigmask(SIG_BLOCK, &set, NULL);
//shParam: signalHandlerParam
signalHParam *shParam = malloc(sizeof(signalHParam));
shParam->set = &set;
shParam->arrayOfThIDs = thids;
s = pthread_create(&signalHandlerThread, NULL, signalHandlerTask, (void *) shParam);
for(int i = 0; i < 2; i ++){
pthread_t dummyThread;
pthread_create(&dummyThread, NULL, &dummyTask, NULL);
thids[i] = &dummyThread;
}
pause();
//pthread_join(signalHandlerThread, NULL);
return 1;
}
As you can see the signalHandlerThread execute a function named signalHandlerTask, which is:
static void *signalHandlerTask(void *shParam){
signalHParam *tmp = (signalHParam *) shParam;
sigset_t *set = tmp->set;
int s, sig;
int i = sigismember(set, SIGINT);
if(i != 1)
printf("error\n");
while(1 == 1){
s = sigwait(set, &sig);
if(sig == SIGINT){
printf("\n----- signal recived ----\n");
//function that use the array to kill threads
killThreads(tmp->arrayOfThIDs);
pthread_exit(NULL); //kill the signal handler thread
}
}
}
The shParam is a struct that I use to pass multiple arguments to the task of the thread (signalHandlerTask) and it is like this
typedef struct{
pthread_t **arrayOfThIDs;
sigset_t *set;
} signalHParam;
Finally we are at the real issue. I created the killThreads function as follows:
void killThreads(pthread_t **thids){
for(int i = 0; i < 2; i++){
int r = pthread_cancel(*thids[i]);
if(r != 0)
printf("error!! %d\n", r);
//r is 3, why??
pthread_join(*thids[i], NULL);
}
}
The problem is, as said, that pthread_cancel(*thids[i])
not work, threads are left alive and I can't figure out why
Here is the entire code for those who wants to run it:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <error.h>
#include <unistd.h>
#include <errno.h>
typedef struct{
pthread_t **arrayOfThIDs;
sigset_t *set;
} signalHParam;
void killThreads(pthread_t **thids){
//termino il threadListener, e tutti i thread nel thread pool
for(int i = 0; i < 2; i++){
//FAI ANCHE SU PROGETTO!!
int r = pthread_cancel(*thids[i]);
if(r != 0)
printf("pthread_cancel failed: %s\n", strerror(r));
//FAI ANCHE SU PROGETTO!!
pthread_join(*thids[i], NULL);
}
}
static void *signalHandlerTask(void *shParam){
signalHParam *tmp = (signalHParam *) shParam;
sigset_t *set = tmp->set;
int s, sig;
int i = sigismember(set, SIGINT);
if(i != 1)
printf("error\n");
while(1 == 1){
s = sigwait(set, &sig);
if(sig == SIGINT){
printf("\n----- signal recived ----\n");
//function that use the array to kill threads
killThreads(tmp->arrayOfThIDs);
pthread_exit(NULL); //kill the signal handler thread
}
}
}
static void * dummyTask(){
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
//printf("mo aspetto 10s\n");
//sleep(10);
while(1){
pthread_testcancel();
printf(" Ciao!\n");
sleep(2);
}
return NULL;
}
int main(){
//need to the signal handler thread, so it can kill all the threads with cancel
pthread_t **thids = malloc( 2 * sizeof(pthread_t *));
sigset_t set;
pthread_t signalHandlerThread;
sigemptyset(&set);
sigaddset(&set, SIGINT);
//s is for error checking, not importanto now
int s = pthread_sigmask(SIG_BLOCK, &set, NULL);
//shParam: signalHandlerParam
signalHParam *shParam = malloc(sizeof(signalHParam));
shParam->set = &set;
shParam->arrayOfThIDs = thids;
s = pthread_create(&signalHandlerThread, NULL, signalHandlerTask, (void *) shParam);
for(int i = 0; i < 2; i ++){
pthread_t dummyThread;
pthread_create(&dummyThread, NULL, &dummyTask, NULL);
thids[i] = &dummyThread;
}
pthread_join(signalHandlerThread, NULL);
return 1;
}
Having run the full program myself, it turns out that the only important bug is the one I originally pointed out in the comments. (There are a bunch of style problems and places where I wouldn't have done it that way, but none of them rises to the level of "bug". There is one minor unrelated bug: you forgot to include stdio.h
and signal.h
.)
for(int i = 0; i < 2; i ++){
pthread_t dummyThread;
pthread_create(&dummyThread, NULL, &dummyTask, NULL);
thids[i] = &dummyThread;
}
This creates a single variable named dummyThread
(whether or not it is declared inside the loop) and writes all of the thread handles into that variable. All of the thids[i]
pointers are set to point to that one variable. Since a single pthread_t
variable can only hold a single thread handle, you lose the thread handles created on all but the last iteration of the loop. Later, the signal handler thread will attempt to cancel the same thread repeatedly, will succeed the first time, and fail the remaining N-1 times. (To make it more obvious what's going on, increase the number of threads and notice that the program prints "pthread_cancel failed: No such process" exactly N-1 times, no matter what N is.)
The correction is simply to use an array of pthread_t
instead of an array of pthread_t *
, and write the thread handles directly into the array:
typedef struct{
pthread_t *arrayOfThIDs;
sigset_t *set;
} signalHParam;
void killThreads(pthread_t **thids){
//termino il threadListener, e tutti i thread nel thread pool
for(int i = 0; i < 2; i++){
//FAI ANCHE SU PROGETTO!!
int r = pthread_cancel(thids[i]);
if(r != 0)
printf("pthread_cancel failed: %s\n", strerror(r));
//FAI ANCHE SU PROGETTO!!
pthread_join(*thids[i], NULL);
}
}
// ...
int main(){
//need to the signal handler thread, so it can kill all the threads with cancel
pthread_t *thids = malloc( 2 * sizeof(pthread_t));
sigset_t set;
pthread_t signalHandlerThread;
sigemptyset(&set);
sigaddset(&set, SIGINT);
//s is for error checking, not importanto now
int s = pthread_sigmask(SIG_BLOCK, &set, NULL);
//shParam: signalHandlerParam
signalHParam *shParam = malloc(sizeof(signalHParam));
shParam->set = &set;
shParam->arrayOfThIDs = thids;
s = pthread_create(&signalHandlerThread, NULL, signalHandlerTask,
(void *) shParam);
for(int i = 0; i < 2; i ++){
pthread_create(&thids[i], NULL, &dummyTask, NULL);
}
pthread_join(signalHandlerThread, NULL);
return 1;
}
signalHandlerTask
and dummyTask
are basically correct as-is.