Below is the example of abort functionality implementation using signals as given in "Advanced Programming in Unix". Few doubts in the below code -
void abort(void) /* POSIX-style abort() function */
{
sigset_t mask;
struct sigaction action;
/*
* Caller can't ignore SIGABRT, if so reset to default.
*/
sigaction(SIGABRT, NULL, &action);
if (action.sa_handler == SIG_IGN) {
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
}
if (action.sa_handler == SIG_DFL)
fflush(NULL); /* flush all open stdio streams */
/*
* Caller can't block SIGABRT; make sure it's unblocked.
*/
sigfillset(&mask);
sigdelset(&mask, SIGABRT); /* mask has only SIGABRT turned off */
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT); /* send the signal */ **STEP 1**
/*
* If we're here, process caught SIGABRT and returned.
*/
fflush(NULL); /* flush all open stdio streams */
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL); /* reset to default */
sigprocmask(SIG_SETMASK, &mask, NULL); /* just in case ... */
kill(getpid(), SIGABRT); /* and one more time */ **STEP 2**
exit(1); /* this should never be executed ... */
}
Question
a. When we send the first kill with SIGABRT (marked by Step 1), why are we expecting the code to continue to next line? (see the comment - 'If we're here, process caught SIGABRT and returned' )
b. Why do we need to deliver kill signal again (in Step 2) and then exit(1) is not supposed to be hit. (refer comment in the code)
Most programs don't do anything particular with SIGABRT.
But some weird programs could install their own signal handler on SIGABRT, and the abort function should still work, even for them.
So most programs -those not catching SIGABRT- won't go past step 1 (because the default behavior for SIGABRT is to dump core, according to signal(7) ...).
The few programs who do catch SIGABRT will go till step 2 (if calling your abort). At that point, the default behavior of SIGABRT has been reinstalled. So the program dump core at step 2. And the final exit(1) cannot be reached.
Related
In a multithreaded C++ program where the main thread is executing a libuv event loop, is it guaranteed that this event loop thread is executing signal handlers registered using uv_signal_start?
Background information:
From http://docs.libuv.org/en/v1.x/design.html
The I/O (or event) loop is [...] meant to be tied to a single thread.
But as we are in a multithreaded program, signal handlers can be executed by other threads
According to POSIX.1, a process-directed signal (sent using kill(2), for example) should be handled by a single, arbitrarily selected thread within the process.
So my question is basically whether libuv signal handling works as advertised
Signal handles implement Unix style signal handling on a per-event loop bases.
even in multithreaded programs.
TLDR: Yes, should work as advertised.
From my understanding of libuv's source code unix/signal.c there is a generic signal handler
static void uv__signal_handler(int signum) {
uv__signal_msg_t msg;
uv_signal_t* handle;
int saved_errno;
saved_errno = errno;
memset(&msg, 0, sizeof msg);
if (uv__signal_lock()) {
errno = saved_errno;
return;
}
for (handle = uv__signal_first_handle(signum);
handle != NULL && handle->signum == signum;
handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
int r;
msg.signum = signum;
msg.handle = handle;
/* write() should be atomic for small data chunks, so the entire message
* should be written at once. In theory the pipe could become full, in
* which case the user is out of luck.
*/
do {
r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg);
} while (r == -1 && errno == EINTR);
assert(r == sizeof msg ||
(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)));
if (r != -1)
handle->caught_signals++;
}
uv__signal_unlock();
errno = saved_errno;
}
in which a pipe handle->loop->signal_pipefd[1] is used to tell the handle's associated loop abount the incoming signal. Indeed, this generic signal handler can be called from any thread, however the libuv thread will then call the user's specific signal handler registered with uv_signal_start in the event loop thread (main thread in my setting) when it reads the signal_pipefd[1] in the next loop iteration.
This was for the unix source code and the windows win/signal.c source has a similar mechanism.
So the answer should be yes, it should also work as advertised in a multithreaded setting, i.e. the registered handler will be executed by the loop thread.
Background
My objective is to handle certain signals on a dedicated thread rather than to have them handled on any of the threads that happen to be running in my process when the signal is raised.
I am doing this as follows (in this example, for signal 16 only):
On the main thread, before any other threads are started (error handling ommited)
sigset_t sigset;
sigaddset(&sigset, 16);
sigprocmask(SIG_BLOCK, &sigset, nullptr);
Then I create a thread that waits for those signals (only 16 in this example):
std::thread _thread = std::thread([&]()
{
int ret = sigwaitinfo(&sigset, nullptr);
if (ret == 16)
{
// handle signal 16
}
});
This works well.
Problem
However, I would like to be able to cancel the call to sigwaitinfo when needed.
Two Inadequate Solutions
I have tried two solutions, but neither are adequate:
1. Polling
One option (that works) is not to use sigwaitinfo but rather to use sigtimedwait which accepts a timeout argument.
This allows me to use polling and to cancel when the call next returns and some cancel flag is set.
The code in the thread then looks like this:
std::atomic<bool> _cancel (false);
std::thread _thread = std::thread([&]()
{
timespec _timespec {0, 1}; // 1 second
int ret = sigtimedwait(&sigset, nullptr, _timespec);
if (_cancel)
{
return;
}
if (ret == 16)
{
// handle signal 16
}
});
In order to cancel, I only need to set the _cancel flag in the main thread.
The problem with this solution, is that polling incurs the typical trade-off between responsiveness (of the cancellation) and the amount of busy work done checking the cancellation flag.
2. raise()/sigqueue()/kill()
In this solution I add to the signal mask a dedicated signal, for instance SIGUSR1 with the following call:
sigset_t sigset;
sigaddset(&sigset, 16);
sigaddset(&sigset, SIGUSR1); // <-- added call here
sigprocmask(SIG_BLOCK, &sigset, nullptr);
Then when I need to cancel the call to sigwaitinfo I set a cancel flag and call raise(SIGUSR1)
The code in the thread then looks like this:
std::atomic<bool> _cancel (false);
std::thread _thread = std::thread([&]()
{
int ret = sigwaitinfo(&sigset, nullptr);
if (_cancel) // <-- now check _cancel flag before handling signal
{
return;
}
if (ret == 16)
{
// handle signal 16
}
});
The cancellation is now done as follows:
_cancel = true; // <-- set the flag before raising the signal
raise(SIGUSR1);
The problem with this solution is that it doesn't work, because the call to raise() does not cause sigwaitinfo to return in the dedicated thread. I believe that according to the documentation it will only raise the signal in the executing thread itself.
sigqueue() and kill() also do not work.
Summary
Is there a way to cause sigwaitinfo to return prematurely, without requiring a loop in which calls to sigtimedwait are called with a timeout?
Use pthread_kill to send a signal to a specific thread.
E.g., instead of raise(SIGUSR1); do:
if(int rc = ::pthread_kill(_thread.native_handle(), SIGUSR1))
// Handle pthread_kill error.
This is the solution I found.
Instead of waiting with sigtimedwait, use signalfd to get a file descriptor that represents the signals to be handled. (sigprocmask or similar need to be called first as with the solution presented in the question).
Call eventfd to return an "event" file descriptor.
Call poll wait on both file descriptors. This blocks. Do so in a loop.
Signal cancellation by writing to the event file descriptor on a different thread.
When poll returns check which file descriptor was signaled by checking the revents fields.
If the event file descriptor was signaled break from the loop.
Else (the signalfd descriptor was signaled) read the signal description and handle the signal by calling the handler. Then loop around calling poll again.
I have verified that this solution is reliable.
More detailed information can be found in the documentation for:
signalfd,eventfd and poll
Consider the next piece of code:
// Sigaction and timers
struct sigaction sa;
sigset_t maskSet, pendingSet;
/**
* Blocks the SIG_SETMASK in maskSet.
* #return None.
*/
static void block_signal(void)
{
// ~~~Blocking signal~~~
if(sigemptyset(&pendingSet) == -1)
{
ErrorHandler::sysCallError(THREAD_SYS_CALL_ERROR_SIGNAL_HANDLE);
}
if(sigpending(&pendingSet) == -1)
{
ErrorHandler::sysCallError(THREAD_SYS_CALL_ERROR_SIGNAL_HANDLE);
}
if(sigprocmask(SIG_BLOCK, &maskSet, NULL) == -1)
{
killProcessAfterMemoryAllocs();
}
}
/**
* Unblocks the SIG_SETMASK in maskSet.
* #return None.
*/
static void unblock_signal(void)
{
// ~~~Blocking signal~~~
int result;
int sig;
// If we got a signal while performing operation that require signal block.
if(sigismember(&pendingSet, SIGVTALRM) != -1)
{
result = sigwait(&pendingSet, &sig);
// DOESNT REACH HERE
if(result == 0)
{
printf("sigwait() returned for signal %d\n", sig);
//Do stuff
}
}
if (sigprocmask(SIG_UNBLOCK, &maskSet, NULL) == -1)
{
killProcessAfterMemoryAllocs();
}
}
My main goal is to be able to run functions, that during their run - SIGVTALARM ,that is raised by a timer i defined, will be blocked. (block_signal -> dosomthing SIGNALS ARE BLOCKED -> unblocksignal). Maskset is initialized with SIGVTALARM
Two questions:
By the way i implemented, result = sigwait(&pendingSet, &sig); causes the program go into an infinite loop.
Without sigwait() - I use a virtual timer that raise SIGVTALARM every defined interval of time.
Suppose i blocked SIGVTALARM. I understand that, as soon (while blocked) as it is raised - the signal becomes pending. And as soon as i unblock it, the signal is recieved and treated by a signal hanler.
What i dont understand is whats going on with the NEXT signal raised. Will the next signal will be raised a defined interval of time after the PREVIOUS signal released, or will it be raised a defined interval of time from the moment the PREVIOUS signal raised (and blocked -> became pending).
I have a main process and some child process spawn from it. At a point of time i have to give SIGINT signal to all the child process but not to main process. I am unable to store pid's for all child processes. So i used SIG_IGN for ignoring SIGINT in main process and set to default after my action. But it is not working.
Please find my code snippet below:
/* Find group id for process */
nPgid = getpgid(parentPID);
/* Ignore SIGINT signal in parent process */
if (signal(SIGINT, SIG_IGN) == SIG_ERR)
{
cout << "Error in ignoring signal \n");
}
/* Send SIGINT signal to all process in the group */
nReturnValue = kill ( (-1 * nPgid), SIGINT);
if (nReturnValue == RETURN_SUCCESS)
{
cout << "Sent SIGINT signal to all process in group successfully \n";
}
else
{
cout << "Alert!!! Unable to send SIGINT signal to all process in the group \n";
}
/* Set SIGINT signal status to default */
signal (SIGINT, SIG_DFL);
sleep(2);
I am not getting any error. But parent is getting killed. Am i doing anything wrong here?
nPgid = getpgid(parentPID);
What is parentPID? The get the group of the calling process either pass 0 or the result of getpid().
From man getpgid():
getpgid() returns the PGID of the process specified by pid. If pid
is zero, the process ID of the calling process is used. (Retrieving
the PGID of a process other than the caller is rarely necessary, and
the POSIX.1 getpgrp() is preferred for that task.)
From this text above I'd draw the conclusion to do
nPgid = getpgid(o);
There is a code that I can't understand if there is any part of the code that causes exiting of the loop without exiting the whole program.
Here is the code:
/* per-packet event loop */
while (true)
{
perf_push (PERF_EVENT_LOOP);
/* wait on tun/socket list */
multi_get_timeout (&multi, &multi.top.c2.timeval);
status = multi_tcp_wait (&multi.top, multi.mtcp);
MULTI_CHECK_SIG (&multi);
/* check on status of coarse timers */
multi_process_per_second_timers (&multi);
/* timeout? */
if (status > 0)
{
/* process the I/O which triggered select */
multi_tcp_process_io (&multi);
MULTI_CHECK_SIG (&multi);
}
else if (status == 0)
{
multi_tcp_action (&multi, NULL, TA_TIMEOUT, false);
}
perf_pop ();
}
/* shut down management interface */
uninit_management_callback_multi (&multi);
Is the last line reachable?
Have a look at the definition of the macro MULTI_CHECK_SIG. I googled for MULTI_CHECK_SIG and found a definition in terms of another macro EVENT_LOOP_CHECK_SIGNAL which contained a break statement.
The loop looks like it has no ending. This is often the case in embedded systems programming. The loop is then simply ended by disconnecting the power supply. Hardware cannot stop working, so there will always be a loop that has no ending condition.
In Linux (and other operating systems) you could also terminate the program by implementing IPC signal functions.