I have a multithreaded program with one detached thread and another
joinable.
I have a SIGINT handler to cancel all the threads, liberate resources
and exit properly.
The problem is that when I join the nondetached thread in the main
program, sometimes, it waits there forever, never returning and never
finishing. However, sometimes, it joins it...
Here is my code:
----------------------------------------------------------------------------------------------
/* Thread Destructors prototypes */
static void closeServerThr(void *args)
{ ...
DBGP("Server Thread terminated\n");
}
void serialCloseDevices (void *param)
{ ...
DBGP("Serial devices closed\n");
}
/* SIGINT handler */
void sigint_handler(int sig)
{
/* SIGINT (Ctrl + C) Handler */
pthread_t thrID[2];
int i = 0; /* index */
/* Cancel the launched threads */
thrID[0] = runServosID;
thrID[1] = serverThrID;
for (i = 0; i < 2; i++) {
if (thrID[i] > 0) {
DBGP("Cancelling thrID %d\n", thrID[i]);
if (pthread_cancel(thrID[i]) == -1) {
err_quit("pthread_cancel threadID =
%d", thrID[i]);
}
}
}
DBGP ("All threads cancelled\n");
// exit(0);
}
/* First thread, DETACHED */
void *runServos(void *parameters)
{ ...
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
/* Destructor for the thread when cancelled */
pthread_cleanup_push(&serialCloseDevices, NULL);
...
while (1) {
...
}
pthread_cleanup_pop(1);
DBGP("runServos going out...\n");
pthread_exit(EXIT_SUCCESS);
}
/* Second thread, JOINABLE */
void *serverThr(void *params)
{ ...
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
...
/* Destructor for the thread when cancelled */
pthread_cleanup_push(&closeServerThr, NULL);
while (1) {
...
}
pthread_cleanup_pop(1);
DBGP("serverThr going out...\n");
pthread_exit(EXIT_SUCCESS);
}
/* Main program */
int main(int argc, char **argv)
{
struct sigaction sa; /* To handle SIGINT */
pthread_attr_t tattr; /* Thread attributes */
/* Programme new handlers for SIGINT */
/* Capture SIGINT (Ctrl + C) signal */
sa.sa_handler = sigint_handler; /* Handler for SIGINT
*/
sa.sa_flags = 0; /* No special flags */
sigaction(SIGINT, &sa, NULL); /* New action for
SIGINT */
/* Attributes for threads */
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
/* Launch thread to run the servos */
if (pthread_create(&runServosID, &tattr, &runServos, NULL) < 0)
err_quit("pthread_create runServos");
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
/* Launch thread to check inputs in System */
if (pthread_create(&serverThrID, &tattr, &serverThr, &sock) <
0)
err_quit("pthread_create serverThr");
if (pthread_join(serverThrID, NULL) == -1)
err_quit("pthread_join serverthr");
DBGP("Server Thread: Id %d joined in main flow\n",
serverThrID);
...
/* Exit */
DBGP("Normal Exit through main flow\n");
return (EXIT_SUCCESS);
}
------------------------------------------------------------------
When it works, after pressing CTRL+c, it says:
Cancelling thrID 2
Cancelling thrID 3
All Threads cancelled
Server Thread terminated
Serial devices closed
Server Thread: Id 3 joined in main flow
Normal Exit through main flow
and finishes :)
Sometimes, after pressing CTRL+c, it only says:
Cancelling thrID 2
Cancelling thrID 3
All Threads cancelled
Server Thread terminated
Serial devices closed
and hangs forever... I have to kill the process from other window. Why
can it be??? It's cancelled and terminated (last line in the
destructor)...
Any help, please?
Thanks,
Dario
> I have a SIGINT handler to cancel all the threads, liberate resources
> and exit properly.
You can do very little in a signal handler.
Calling any pthread_* function is a big no-no: it leads to undefined
behaviour, which is exactly what you got:
> The problem is that when I join the nondetached thread in the main
> program, sometimes, it waits there forever, never returning and never
> finishing. However, sometimes, it joins it...
Rewrite the code not to call async-signal-unsafe functions from
async-signal context.
Common solution is to set an int flag in the handler, and to check
that flag in the "while(1)" loops.
Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.
Strictly speaking, it should be a 'volatile sig_atomic_t' flag. An int
is likely to work on most platforms, but you might as well use something
that POSIX guarantees to work.
--
David Hopwood <david.nosp...@blueyonder.co.uk>
> Rewrite the code not to call async-signal-unsafe functions from
> async-signal context.
>
> Common solution is to set an int flag in the handler, and to check
> that flag in the "while(1)" loops.
Another common solution is to block the signal in every thread but one
thread whose sole purpose is to handle signals. That thread can handle
signals synchronously without a signal handler (since it has nothing
else to do), so you can call async-signal-unsafe functions.
DS
Thanks a lot to all of you :)
I've used a 'volatile sig_atomic_t' termination_signal_flag. The SIGINT
handler only contains a
/* SIGINT handler */
void sigint_handler(int sig)
{
termination_signal_flag = 1;
}
and instead of the while(1) of the threads, I've written a
while (!termination_signal_flag) {
...
}
It works perfectly :)
One thread I keep it as joinable and the other detached. I fistly join
the joinable one and later detach the detached one. Thus, the program
exits properly after all the thread cleanups and the main program
itself.
Thanks, again. I'm not used to all this async-safe/unsafe staff of
signals and threads...
Cheers,
Dario
You need to block the signal in all threads if you are going to do that, and
have one wait on sigwait(). If you leave the signal unblocked in one
thread, the signal handler (if there is one) will execute asynchronously in
that thread and the async-signal-safe requirements will still apply.
Chris