My code runs with multi-threads. In the meantime, there is a signal_handler
function which handle the external ctrl-c
signal.
I found the signal_handler
function runs on the thread which raise
the signal.
But i want to handle it on main thread no matter who raise
it.
here is a demo code:
#include <csignal>
#include <iostream>
#include <thread>
std::thread::id main_id;
void signal_handler(int sig) {
// here always run in subthread, I want to make it run in main thread
std::thread::id this_id = std::this_thread::get_id();
std::cout << this_id << " got signal\n";
signal(sig, SIG_DFL);
exit(sig);
}
void thread_fn() {
while (1) {
std::cout << "subthread " << std::this_thread::get_id() << " printing\n";
std::this_thread::sleep_for(std::chrono::seconds(5));
raise(SIGINT);
}
}
int main() {
signal(SIGINT, signal_handler);
std::thread t(&thread_fn);
main_id = std::this_thread::get_id();
while (1) {
std::cout << "main " << main_id << " printing\n";
std::this_thread::sleep_for(std::chrono::seconds(5));
}
t.join();
}
could you help on this? I am very stingy which means i dont want to create a single thread to wait thread.
Technically signals are delivered to individual threads and not the process itself. In order to invoke a signal handler on another thread, you need to use pthread_kill()
and supply the id of the thread where the signal handler should be executed.
So if I modify your code just a tiny bit to use the low-level functions (because there's no std::thread
based wrapper around pthread_kill()
):
#include <csignal>
#include <iostream>
#include <thread>
#include <pthread.h>
pthread_t main_thread_raw_id;
void signal_handler(int sig)
{
// ... [see comments below]
}
void thread_fn() {
while (1) {
std::cout << "subthread " << std::this_thread::get_id() << " printing\n";
std::this_thread::sleep_for(std::chrono::seconds(5));
pthread_kill(main_thread_raw_id, SIGINT);
}
}
int main() {
signal(SIGINT, signal_handler);
std::thread t(&thread_fn);
main_thread_raw_id = pthread_self();
auto main_id = std::this_thread::get_id();
while (1) {
std::cout << "main " << main_id << " printing\n";
std::this_thread::sleep_for(std::chrono::seconds(5));
}
t.join();
}
Then the main thread will get delivered the signal. (Note that both std::this_thread::get_id()
and pthread_self()
are considered opaque and there is no guarantee that they are equal to each other, even if they might be on your system.)
That all said:
As others have pointed out, using cout
from within a signal handler is not safe and can lead to all sorts of issues. This applies to many functions you might use, starting from everything that allocates on the heap (via new
or malloc()
), up to mutexes, etc. Even exit()
is not safe, only _exit()
is, because exit()
may call destructors registered with atexit()
(and also C++ destructors), while _exit()
skips those.
All of these restrictions make signal handlers very hard to write correctly, and most people who use them just set an atomic flag or something similar, so that it's easy to reason that the handler itself is safe.
Going even further, unless it's some very specific signals such as SIGTSTP
where that isn't feasible in practice, many modern software on Linux doesn't even want to use signal handlers, but rather register a signalfd()
with an event loop.
On Windows, you're completely out of luck here: Microsoft implements signal()
only because the C standard requires them to - and they implement it differently:
SIGTERM
/SIGILL
is never generatedabort()
or raise(signal)
, that thread will execute the signal handler.SIGINT
, a new thread will be created on Windows that invokes the signal handler.Basically, any program that uses signal()
only to the extent that the C standard defines it, will work with Microsoft's implementation thereof, but anything that relies on actual POSIX semantics will not. Windows provides a ton of other mechanisms for interacting with threads (from APCs to suspending and directly modifying the thread's execution state) where you could probably emulate the behavior in some manner, but Windows decidedly does not implement POSIX thread semantics.
See the comments in this question for more details here.
You appear to want to use signals as some kind of thread synchronization mechanism here. And while that is certainly possible, and in some edge cases it might even be useful (e.g. when real-time threads are involved), I would strongly recommend against using signal handlers for this purpose.
What is the actual problem you are trying to solve here?