Reputation: 87
I am new to C, so question may be quite silly.
I am writing socket server that operates data sent from clients. When it gets connection it creates thread that will work with the request.
Also it defines signal handlers. When SIGUSR1 is received it prints debug info to the logfile. The question is: what happens to threads when signal handling function is running?
If threads continue running their jobs, is there a way to freeze them while debugging handler is working?
I tried to find an answer in manpages for pthreads(7) but had not understood this moment. Sorry for possible incorrect terminology.
Upvotes: 2
Views: 1830
Reputation: 1829
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.
Process-directed Signals: A process-directed signal is one that is targeted at (and thus pending for) the process as a whole. A process-directed signal may be delivered to any one of the threads that do not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.
Thread-directed Signals: A thread-directed signal is one that is targeted at a specific thread. The set will consist of the union of the set of pending process-directed signals and the set of signals pending for the calling thread.
You can configure your program to tell how to deal with signals. You can ignore them (few can't be ignored), register a signal handler which will be invoked when that specific signal is received (asynchronous
), or block it to deal with it later (synchronous
).
Coming to your case,
"The question is: what happens to threads when signal handling function is running?"
The signal is delivered once to any thread that is configured to receive it. The thread, which is asynchronously handling the signal, stops whatever it is doing and jumps to the configured signal handler. The flow of the execution in the remaining threads is unaffected.
If threads continue running their jobs, is there a way to freeze them while debugging handler is working?
There is no standard way of doing this. You need to build your own mechanism for enabling this.
To research further, some clarity is required regarding where the debug handler is executed. In each thread or in main()
or in a specific thread?
Assuming main()
implements the logging functionality, below tries to implement the base minimal for the same. Comments are added which enables to walk through code and understand the implementation.
#define THREAD_MAX_COUNT 100
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int debug;
sigset_t debug_mask;
pthread_t main_tid;
void* thread_func(void* th_data)
{
/* .... */
for ( ; ; ) {
if (debug) { // If debug procedure starts
printf("Freezing %d\n", *((int*) th_data));
pthread_kill(main_tid, SIGRTMIN); // Notify the main thread about the thread's freeze.
int signo;
sigwait(&debug_mask, &signo); // Wait till logging is done. main() will signal once it is done.
printf("Resuming %d\n", *((int*) th_data));
}
/* ... */
}
return NULL;
}
int main() {
/* Block SIGINT SIGRTMIN*/
sigset_t sigmask;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGINT);
sigaddset(&sigmask, SIGRTMIN);
pthread_sigmask(SIG_BLOCK, &sigmask, NULL);
/* Set debug variables */
debug = 0;
sigemptyset(&debug_mask);
sigaddset(&debug_mask, SIGRTMIN);
main_tid = pthread_self();
/* Get signalfd for SIGINT */
int sigfd = signalfd(-1, &sigmask, 0);
struct signalfd_siginfo sigbuf;
/* Select variable initializations */
fd_set rd_set, tr_set;
FD_ZERO(&rd_set);
FD_SET(sigfd, &rd_set);
int td_count = 0;
pthread_t tids[THREAD_MAX_COUNT];
for ( ; ; ) {
/* Wait for signal */
tr_set = rd_set;
select(sigfd + 1, &tr_set, NULL, NULL, NULL);
if (FD_ISSET(sigfd, &tr_set)) {
/* Read the pending signal */
read(sigfd, &sigbuf, sizeof(sigbuf));
/* Start logging */
debug = 1;
int signo;
for (int count = 0; count < td_count; count++) {
/* Wait for all threads to freeze */
sigwait(&debug_mask, &signo);
}
printf("Logging...\n");
sleep(3);
/* End logging and resume threads */
debug = 0;
for (int count = 0; count < td_count; count++)
pthread_kill(tids[count], SIGRTMIN);
/* Note below code is for testing purpose; Creates new thread on each interruption */
int* td_data = malloc(sizeof(int));
*td_data = td_count;
pthread_create(tids + td_count, NULL, thread_func, td_data);
td_count++;
}
}
return 0;
}
Terminal Session:
$ gcc SO.c -lpthread
$ ./a.out
^CLogging...
^CFreezing 0
Logging...
Resuming 0
^CFreezing 0
Freezing 1
Logging...
Resuming 0
Resuming 1
^CFreezing 0
Freezing 1
Freezing 2
Logging...
Resuming 1
Resuming 0
Resuming 2
^CFreezing 2
Freezing 3
Freezing 1
Freezing 0
Logging...
Resuming 1
Resuming 3
Resuming 2
Resuming 0
^CFreezing 1
Freezing 4
Freezing 3
Freezing 0
Freezing 2
Logging...
Resuming 1
Resuming 2
Resuming 0
Resuming 4
Resuming 3
^CFreezing 3
Freezing 0
Freezing 4
Freezing 2
Freezing 5
Freezing 1
Logging...
Resuming 0
Resuming 1
Resuming 2
Resuming 5
Resuming 3
Resuming 4
^\Quit (core dumped)
Upvotes: 1
Reputation: 121377
When SIGUSR1 is received it prints debug info to the logfile. The question is: what happens to threads when signal handling function is running?
One of the threads (usually it's the main thread on Linux - but this isn't guaranteed or portable assumption) in the process handles the signal while others continue to run1.
If threads continue running their jobs, is there a way to freeze them while debugging handler is working?
There's no straight-forward way to achieve this. You could implement a mechanism such as, the thread that handles the SIGUSR1 informs other threads to wait (e.g. with a conditional variable) and then once the signal is processed, it could inform other threads to continue. But I wouldn't recommend this because a signal handler should do as small as functionality as possible. Having complex functionality in a signal handler is generally considered bad. You should instead redesign such that other threads don't have to stop (what's the issue if other threads continue while debug info is logged?).
1 The usual way to deal with this is to have dedicated thread that handles signals. i.e. block the signals you're interested in and create a thread (before creating other threads) that handles those signals. pthread_sigmask(3)
has an example of how to do it.
-
Upvotes: 3