Signal Handler Function in Multithreaded Environment

signal handler function in multithreaded environment

Be very careful: as the signal(7) page is telling, only very few functions (the "async-signal-safe" ones, see signal-safety(7) for more) can be (directly or indirectly) called inside signal handlers. Mutex related functions probably should not be called in signal handlers. See also pthreads(7)

You might consider setting a volatile sigatomic_t variable in your signal handler, and test the value of that flag from time to time.
If you have C++11 (or C11) atomics, e.g. C++11 std::atomic or C11 <stdatomic.h>, you could make that volatile variable also atomic in that sense. Then use the atomic load facilities to test it.

The Qt documentation suggests the following trick: create a pipe(2) to self at startup, then have your signal handler write(2) (the write syscall is specified as being async-signal-safe) a single (or more) byte[s] to a pipe to your same process, and have your GUI event loop poll(2) the read end of that pipe.

A Linux-specific way to handle signals with Qt might be to use signalfd(2) probably with QSocketNotifier (despite the name, it works on pollable file descriptors, not only sockets). With other GUI toolkits, you probably can also add a file descriptor (the one from signalfd or pipe) to be polled.

Signal handler behavior in multi threaded environment

From the manpage for 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.

So all threads share the same handlers, yes. If you use pthread_kill() to send a signal to a specific thread, that thread should execute the handler (Depending on the thread's signal mask set with pthread_sigmask(), of course).

Also note that you can't safely use printf() or other stdio functions in a signal handler. See the list of allowed functions in signal-safety(7).

Multithreaded Environment - Signal Handling in c++ in unix-like environment (freeBSD and linux)

I am writing this for those who had faced with this problem. My problem was about synchronization of threads. After i got handle synchronization problem, the program now, can handle the signals. My advice is check the synchronization again and make sure that it works correctly.

I am sorry for late answer.

Edited :
I have also used sigaction for signal handling
and I have change my global bool variable whit this definition :

static volatile sig_atomic_t exitFlag = 0;

This flag has been used for checking whether the signal received or not.

trapping signals in a multithreaded environment

Using syscall(SYS_gettid) works for me on my Linux box: gcc pt.c -lpthread -Wall -Wextra

//pt.c
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <ucontext.h>
#include <stdlib.h>

static sigjmp_buf jmpbuf[65536];

static void handler(int sig, siginfo_t *siginfo, void *context)
{
//ucontext_t *ucontext = context;
pid_t tid = syscall(SYS_gettid);

printf("Thread %d in handler, signal %d\n", tid, sig);
siglongjmp(jmpbuf[tid], 1);
}

static void *threadfunc(void *data)
{
int index, segvindex = *(int *)data;
pid_t tid = syscall(SYS_gettid);

for(index = 0; index < 500; index++) {
if (sigsetjmp(jmpbuf[tid], 1) == 1) {
printf("Recovery of thread %d\n", tid);
continue;
}
printf("Thread %d, index %d\n", tid, index);
if (index % 5 == segvindex) {
printf("%zu\n", strlen((char *)2)); // SIGSEGV
}
pthread_yield();
}
return NULL;
}

int main(void)
{
pthread_t thread1, thread2, thread3;
int segvindex1 = rand() % 5;
int segvindex2 = rand() % 5;
int segvindex3 = rand() % 5;
struct sigaction sact;

memset(&sact, 0, sizeof sact);
sact.sa_sigaction = handler;
sact.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &sact, NULL) < 0) {
perror("sigaction");
return 1;
}
pthread_create(&thread1, NULL, &threadfunc, (void *) &segvindex1);
pthread_create(&thread2, NULL, &threadfunc, (void *) &segvindex2);
pthread_create(&thread3, NULL, &threadfunc, (void *) &segvindex3);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
return 0;
}

To be more portable pthread_self can be used. It is async-signal-safe.

But the thread which got the SIGSEGV should start a new thread by async-signal-safe means and should not do a siglongjmp as it could result in the invocation of non-async-signal-safe functions.

libuv signal handling 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.

Accessing shared data from a signal handler

I read through the article that you reference and found some interesting information in the "Threads in Signal Handlers" section. In that section, you'll see that they have a list of Posix function calls that can be made from within signal handlers. Then soon after that list, they mention the following:

But where are the Pthreads calls? They're not in either of these
lists! In fact, the Pthreads standard specifies that the behavior of
all Pthreads functions is undefined when the function is called from a
signal handler. If your handler needs to manipulate data that is
shared with other threads≈buffers, flags, or state variables≈it's out
of luck. The Pthreads mutex and condition variable synchronization
calls are off limits.

Notice the last sentence: "Pthreads mutex and condition variable synchronization calls are off limits"

The aforementioned functions that can be called from a signal handler are described as follows:

These functions have a special property known as reentrancy that
allows a process to have multiple calls to these functions in progress
at the same time.

The pthread synchronization functions dont have the special property known as reentrancy, so I imagine that if these functions (pthread_mutex_lock() for instance) are interrupted by an arriving signal, then the behavior is not "safe".

Imagine that your application calls pthread_mutex_lock(&theMutex) and at exactly that moment (that is, while in the pthread_mutex_lock() function) a signal arrives. If the signal handler also calls pthread_mutex_lock(&theMutex), the previous pthread call may not have terminated, so it cant be guaranteed which call to pthread_mutex_lock() will get the lock. So the resulting behavior will be undefined/undeterministic.

I would imagine that the call to sigwait() from a particular thread would guarantee that no important, non-reentrancy function calls may get interrupted, thus allowing calls to the pthread synchronization functions to be "safe".

multi-threaded signal handling

According to man 7 signal, all threads in the process share the same signal handler, and if a signal is delivered to a process with multiple threads that have not blocked the signal, one of them is arbitrarily chosen to receive it.

Having a multi-CPU machine will not change these semantics.

In C++, can I use multithreading tools to synchronize UNIX signals with my main program?

No.

The only functions you can safely call in signal handlers are async-signal-safe functions. For Linux, you can find a list of such functions in the signal.7 man page, under "Async-signal-safe functions", a bit more than halfway down the page.

Any function not listed as an async-signal-safe can not be safely called in a signal handler.

Note that the only synchronization function listed as async-signal-safe on Linux is sem_post().

Shutting down a multithreaded application by installing a signal handler

What happens is that your main function exits immediately destroying global handler object and then testClass. Then the main thread gets blocked in TestClass::~TestClass. The signal handler ends up accessing already destroyed objects, which leads to the undefined behaviour.

The root cause is undefined object ownership due to shared pointers - you do not know what and when ends up destroying your objects.


A more general approach is to use another thread to handle all signals and block signals in all other threads. That signal handling thread then can call any functions upon receiving a signal.

You also do not need the smart pointers and function wrappers here at all.

Example:

class TestClass
{
public:
TestClass();
~TestClass();
void shutDown();

TestClass(const TestClass&) = delete;
TestClass& operator=(const TestClass&) = delete;

private:
void postResults();
std::string getResult();
void processResults();

std::mutex outQueueMutex;
std::condition_variable outQueueConditionVariable;
std::queue<std::string> outQueue;
bool stop = false;

std::thread threadA;
std::thread threadB;
};

TestClass::TestClass()
: threadA(std::thread(&TestClass::processResults, this))
, threadB(std::thread(&TestClass::postResults, this))
{}

TestClass::~TestClass() {
threadA.join();
threadB.join();
}

void TestClass::postResults() {
while(true) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
std::string name = "ABCDEF";
{
std::unique_lock<std::mutex> lock(outQueueMutex);
if(stop)
return;
outQueue.push(name);
outQueueConditionVariable.notify_one();
}
}
}

void TestClass::shutDown() {
std::unique_lock<std::mutex> lock(outQueueMutex);
stop = true;
outQueueConditionVariable.notify_one();
}

std::string TestClass::getResult() {
std::string result;
{
std::unique_lock<std::mutex> lock(outQueueMutex);
while(!stop && outQueue.empty())
outQueueConditionVariable.wait(lock);
if(stop)
return result;
result= outQueue.front();
outQueue.pop();
}
return result;
}

void TestClass::processResults()
{
while(true) {
const auto result = getResult();
if(result.empty())
break;
std::cout << result << std::endl;
}
}

int main() {
// Block signals in all threads.
sigset_t sigset;
sigfillset(&sigset);
::pthread_sigmask(SIG_BLOCK, &sigset, nullptr);

TestClass testClass;

std::thread signal_thread([&testClass]() {
// Unblock signals in this thread only.
sigset_t sigset;
sigfillset(&sigset);
int signo = ::sigwaitinfo(&sigset, nullptr);
if(-1 == signo)
std::abort();

std::cout << "Received signal " << signo << '\n';
testClass.shutDown();
});

signal_thread.join();
}

How do handlers for CtrlEvents work in a multithreaded environment?

None of the above.

As described in the documentation for the handler routine:

When the signal is received, the system creates a new thread in the process to execute the function.

If you need a particular thread to respond to the console control signal, it is your responsibility to write a handler routine to contact that thread, using whatever inter-thread communications method is most appropriate to your code.

It is also your responsibility to ensure that the handler routine is thread-safe, since the existing threads will continue to run as normal while the signal is being processed.


Please note that console control signals are not closely analogous to POSIX signals. For one thing, they generally only affect console applications (a GUI application will not receive console control signals) and for another they are not designed or intended to be used as an IPC mechanism. Plus, of course, they never interrupt an existing thread.

The Microsoft C runtime does implement C signals, as required by the standard, but these aren't much like POSIX signals either; most notably, they are intra-process only. As a special case, if you configure a SIGINT handler, it is implemented as a console control handler, but this functionality is officially unsupported and I recommend against using it.



Related Topics



Leave a reply



Submit