Toggling logging functionality using signals on a multithreaded program - c++

I am looking to put in some code (temporary at the moment, but as a thought experiment, I am considering working it in long term), that would accomplish two things.
Allow toggling of logs via signals.
Trap all catchable signals and output pid/thread/signal info to the logs, followed by returning to default behavior.
here is some compiling pseudo-code which shows what I would like to achieve.
#include <iostream>
#include <csignal>
#include <thread> // for this_thread
#include <unistd.h> // For gitpid() and fork()
#include <cstdlib>
// What is a save way of determining the number of
// available signals
// ** Externally defined values
const int verbose_debug_signal = 30;
const int trace_logging_enabled_default = 0;
// ** Component Control Class Definition Start
volatile sig_atomic_t trace_logging_enabled
= trace_logging_enabled_default;
void handle_signal(int sig)
{
std::cout << "Process PID: " << std::dec << getpid()
<< " Caught signal " << std::dec
<< sig << " on thread 0x" << std::hex
<< std::this_thread::get_id() << "\n";
if(sig == verbose_debug_signal)
{
trace_logging_enabled = ~trace_logging_enabled;
std::cout << "Trace Logging "
<< (!!trace_logging_enabled ? "enabled\n" : "disabled\n");
}
else
{
signal(sig, SIG_DFL);
raise(sig);
}
}
class ComponentControlClass
{
public:
ComponentControlClass()
: trace_logging_enabled_ptr(&trace_logging_enabled)
{}
~ComponentControlClass(){}
private:
volatile sig_atomic_t *trace_logging_enabled_ptr;
};
int main()
{
for(int i = 1; i < NSIG; ++i)
{
signal(i, handle_signal);
}
// BAH! stupid SIP strikes again. I think I would
// have to sign this to make it work.
// pid_t children[3];
// for(auto child : children)
// children[child] = fork();
while(1);
return 0;
}
Compiled with the -O3 flag to make sure Volatile fields don't get clobbered. The result of running the code in window 1 and sending a "kill -30 " in window 2, exactly 4 times:
Process PID: 46008 Caught signal 30 on thread 0x0x11325f600
Trace Logging enabled
Process PID: 46008 Caught signal 30 on thread 0x0x11325f600
Trace Logging disabled
Process PID: 46008 Caught signal 30 on thread 0x0x11325f600
Trace Logging enabled
Process PID: 46008 Caught signal 30 on thread 0x0x11325f600
Trace Logging disabled
This is exactly what I hoped would happen. In this example this class would represent a component control class which is the entry point for our shared objects using a homegrown framework. In other words, this is where I would need to pass external elements to the schema, like a pointer to volatile memory indicating the log state?
My question(s)
I've never seen anyone do anything like this so I am certain there is a good reason. I am trying to understand the implications.
I found a few articles on complex signal handling, and I was having trouble determining what applied. For all other signals aside from the one I am using to toggle I am trapping and re-enabling, so I wouldn't think this would be an issue. Is it?
When the toggling signal is sent I would want to propagate the logging state to
child processes. I haven't been able to simulate this easily on my Mac because of
SIP, but I haven't quite figured out how I would do this. Only confirmation that it won't happen by itself, because signals work on a per-thread basis.
Now that I'm bouncing between linux and Mac, I am seeing that signal.h is pretty different. My main concern is that different macros are used for the max interrupt and apple seems to only have 32 interrupts where linux uses 64. Is there a boilerplate way of approaching the signals (specifically the max/min values) which is more portable? Something like this but hopefully more robust:
int sigma = 0;
#ifdef LINUX
sigmax = _NSIG;
#elif APPLE
sigmax = NSIG;
#endif

Related

Starting/stopping function using QT GUI buttons

I am writing a simple Qt C++ GUI window for the user to input certain values for a USRP device to record (i.e. input start frequency, stop frequency, etc..). Once the user inputted the values, the "EXECUTE" button is clicked and the execute_run_usrp() function is called in its own thread (so not to block the GUI). Then the STOP button should be able to be clicked at any time to terminate the thread running the function that runs the USRP, execute_run_usrp(), thus terminating the USRP recording process.
The function run_usrp(x,y,z,etc) is defined in another *.cpp file in the Project.
The problem that I am having that the STOP button when clicked only seems to "pause" the function...doesn't actually kill it (like with CTRL-C, which works great here)
Here is my code from MainWindow.cpp for the EXECUTE button click:
// run the following when EXECUTE button is clicked
void MainWindow::on_button_EXECUTE_clicked()
{
if ( ui->calculated_StartTime->text() == "" )
{
QMessageBox messageBox;
messageBox.critical(0,"Error","Hit the \"CALCULATE SCHEDULE\" button first above!!");
messageBox.setFixedSize(500,200);
return;
}
ui->button_STOP->setVisible(true);
ui->button_EXECUTE->setVisible(false);
auto function = std::bind([this]{execute_run_usrp();});
QThread* temp = QThread::create(function);
temp->start();
connect( ui->button_STOP, SIGNAL(clicked()), temp, SLOT( terminate() ));
}
Here is the execute_run_usrp() function:
void MainWindow::execute_run_usrp()
{
float startFreq = ui->input_startFreq->text().toFloat();
float stopFreq = ui->input_stopFreq->text().toFloat();
float stepFreq = ui->input_stepFreq->text().toFloat();
int nRepeats = ui->input_numRepeats->text().toInt();
float ipp = ui->input_IPP->text().toFloat();
int sweepCadence = ui->calculated_sweepCadence->text().toInt();
int numSweeps = ui->input_numSweeps->text().toInt();
std::string schedule_run = ui->calculated_StartTime->text().toStdString();
std::cout << startFreq << std::endl;
std::cout << stopFreq << std::endl;
std::cout << stepFreq<< std::endl;
std::cout << nRepeats << std::endl;
std::cout << ipp << std::endl;
std::cout << sweepCadence << std::endl;
std::cout << numSweeps << std::endl;
run_usrp(startFreq, stopFreq, stepFreq, nRepeats, ipp, sweepCadence, numSweeps, schedule_run);
}
And here is the STOP button code:
void MainWindow::on_button_STOP_clicked()
{
ui->button_STOP->setVisible(false);
ui->button_EXECUTE->setVisible(true);
}
Clicking the STOP button only seems to pause the function, doesn't actually kill it like doing CTRL-C with the keyboard. I think the UHD library (that runs the USRPs) spwans its own thread for running.
Question: How do I 100% terminate the entire function (and including any spwaned children from UHD) when I hit the STOP button ?
Thank you very much!
How do I 100% terminate the entire function (and including any spwaned children from UHD) when I hit the STOP button ?
From the code you've shown there's no easy answer imho.
The shortest one is: by incorporating some inter-thread communication inside the USRP function, provided you want some sort of graceful exit.
As for terminate, QT's docs state:
This function is dangerous and its use is discouraged. The thread can be terminated at any point in its code path. Threads can be terminated while modifying data. There is no chance for the thread to clean up after itself, unlock any held mutexes, etc. In short, use this function only if absolutely necessary.
Alternatively, the whole USRP function can be run inside a separate process, that is then killed. Again, plain killing of the process is far from graceful, still it seems easier and safer than doing it to a thread. You may want to check QProcess for reference.

Kill process by pid with boost process

I do want to kill a process I have a pid of, so my code is the following:
pid_t pid = 28880;
boost::process::child process { pid };
std::cout << "Running: " << process.running() << "\n";
process.terminate();
I noticed though that running() always returns false (no matter what pid I take) and based on the source code then terminate isn't even called.
Digging a little bit deeper it seem to be the linux function waitpid is called. And it always returns -1 (which means some error has occured, rather than 0, which would mean: Yes the process is still running).
WIFSIGNALED return 1 and WTERMSIG returns 5.
Am I doing something wrong here?
Boost process is a library for the execution (and manipulation) of child processes. Yours is not (necesarily) a child process, and not created by boost.
Indeed running() doesn't work unless the process is attached. Using that constructor by definition results in a detached process.
This leaves the interesting question why this constructor exists, but let's focus on the question.
Detached processes cannot be joined or terminated.
The terminate() call is a no-op.
I would suggest writing the logic yourself - the code is not that complicated (POSIX kill or TerminateProcess).
If you want you can cheat by using implementation details from the library:
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main(int argc, char** argv) {
for (std::string_view arg : std::vector(argv + 1, argv + argc)) {
bp::child::child_handle pid{std::atoi(arg.data())};
std::cout << "pid " << pid.id();
std::error_code ec;
bp::detail::api::terminate(pid, ec);
std::cout << " killed: " << ec.message() << std::endl;
}
}
With e.g.
cat &
./sotest $!
Prints

boost::fiber scheduling - when and how

According to the documentation
the currently-running fiber retains control until it invokes some
operation that passes control to the manager
I can think about only one operation - boost::this_fiber::yield which may cause control switch from fiber to fiber. However, when I run something like
bf::fiber([](){std::cout << "Bang!" << std::endl;}).detach();
bf::fiber([](){std::cout << "Bung!" << std::endl;}).detach();
I get output like
Bang!Bung!
\n
\n
Which means control was passed between << operators from one fiber to another. How it could happen? Why? What is the general definition of controll passing from fiber to fiber in the context of boost::fiber library?
EDIT001:
Cant get away without code:
#include <boost/fiber/fiber.hpp>
#include <boost/fiber/mutex.hpp>
#include <boost/fiber/barrier.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/algo/work_stealing.hpp>
namespace bf = boost::fibers;
class GreenExecutor
{
std::thread worker;
bf::condition_variable_any cv;
bf::mutex mtx;
bf::barrier barrier;
public:
GreenExecutor() : barrier {2}
{
worker = std::thread([this] {
bf::use_scheduling_algorithm<bf::algo::work_stealing>(2);
// wait till all threads joining the work stealing have been registered
barrier.wait();
mtx.lock();
// suspend main-fiber from the worker thread
cv.wait(mtx);
mtx.unlock();
});
bf::use_scheduling_algorithm<bf::algo::work_stealing>(2);
// wait till all threads have been registered the scheduling algorithm
barrier.wait();
}
template<typename T>
void PostWork(T&& functor)
{
bf::fiber {std::move(functor)}.detach();
}
~GreenExecutor()
{
cv.notify_all();
worker.join();
}
};
int main()
{
GreenExecutor executor;
std::this_thread::sleep_for(std::chrono::seconds(1));
int i = 0;
for (auto j = 0ul; j < 10; ++j) {
executor.PostWork([idx {++i}]() {
auto res = pow(sqrt(sin(cos(tan(idx)))), M_1_PI);
std::cout << idx << " - " << res << std::endl;
});
}
while (true) {
boost::this_fiber::yield();
}
return 0;
}
Output
2 - 1 - -nan
0.503334 3 - 4 - 0.861055
0.971884 5 - 6 - 0.968536
-nan 7 - 8 - 0.921959
0.9580699
- 10 - 0.948075
0.961811
Ok, there were a couple of things I missed, first, my conclusion was based on misunderstanding of how stuff works in boost::fiber
The line in the constructor mentioned in the question
bf::use_scheduling_algorithm<bf::algo::work_stealing>(2);
was installing the scheduler in the thread where the GreenExecutor instance was created (in the main thread) so, when launching two worker fibers I was actually initiating two threads which are going to process submitted fibers which in turn would process these fibers asynchronously thus mixing the std::cout output. No magic, everything works as expected, the boost::fiber::yield still is the only option to pass control from one fiber to another

SIGINT handler reset in Visual C++ 2015

Consider the following test program
#include <csignal>
#include <iostream>
volatile std::sig_atomic_t signal_raised = 0;
void set_signal_raised(int signal) {
signal_raised = 1;
}
void check(std::sig_atomic_t expected) {
if (signal_raised != expected) {
std::cerr << signal_raised << " != " << expected << std::endl;
abort();
}
}
int main() {
using namespace std;
check(0);
std::signal(SIGINT, set_signal_raised);
check(0);
std::raise(SIGINT);
check(1);
signal_raised = 0;
check(0);
std::raise(SIGINT);
check(1);
cerr << "OK.\n";
}
With GCC and Clang, it outputs "OK." However, with Visual Studio 2015, it outputs nothing.
The signal handler is reset after handling the first signal. This can be verified by adding
auto prev = std::signal(SIGINT, set_signal_raised);
if (prev != set_signal_raised) {
std::cerr << "Unexpected handler." << std::endl;
abort();
}
to the check function. Is this allowed and expected?
Reset of the signal disposition is the behaviour that Unix System V used. But the BSD (currently glibc) do not reset the signal disposition. Either behaviour is allowed by POSIX standard allowed. C standard doesn't specify whether the "reset" is allowed.
From signal(2):
POSIX.1 solved the portability mess by specifying sigaction(2), which
provides explicit control of the semantics when a signal handler is
invoked; use that interface instead of signal().
In the original UNIX systems, when a handler that was established
using signal() was invoked by the delivery of a signal, the
disposition of the signal would be reset to SIG_DFL, and the system
did not block delivery of further instances of the signal. This is
equivalent to calling sigaction(2) with the following flags:
sa.sa_flags = SA_RESETHAND | SA_NODEFER;
System V also provides these semantics for signal(). This was bad
because the signal might be delivered again before the handler had a
chance to reestablish itself. Furthermore, rapid deliveries of the
same signal could result in recursive invocations of the handler.
So, it seems Visual studio follows the System V behaviour.
Is this allowed and expected?
It's allowed but certainly not desired. For this reason, POSIX introduced sigaction(). If you have sigaction() then use it.
Otherwise, you just need to reinstall the handler everytime inside the signal handler:
void set_signal_raised(int signal) {
std::signal(SIGINT, set_signal_raised);
signal_raised = 1;
}

POSIX Threads behaviour different between HP-UX and Solaris 10

I'm migrating a multi threaded application from HP-UX to Solaris and so far, everything is OK except for one thing! The application has a thread that is handling the signals and, when some of them are received, it runs some cleaning (logging, kill child processes and so on).
I've reduced the code as much as it was possible to make a somehow simple example showing the problem:
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <synch.h>
#include <iostream>
#include <unistd.h>
using namespace std;
pthread_t m_signalHandlerThread;
sigset_t m_signalSet;
void signalHandler()
{
while ( true )
{
cout << "SigWait..." << endl;
sigwait( &m_signalSet, &sig );
cout << "Signal!! : " << sig << endl;
break;
}
cout << "OUT" << endl;
}
void* signalHandlerThreadFunction( void* arg )
{
signalHandler();
return (void*)0;
}
int main()
{
sigemptyset( &m_signalSet );
sigaddset( &m_signalSet, SIGQUIT ); //kill -QUIT
sigaddset( &m_signalSet, SIGTERM ); //kill
sigaddset( &m_signalSet, SIGINT ); //ctrl-C
sigaddset( &m_signalSet, SIGHUP ); //reload config
if ( pthread_create( &m_signalHandlerThread, NULL, signalHandlerThreadFunction, NULL ) )
{
cout << "cannot create signal handler thread, system shut down.\n" << endl;
}
int iTimeout = 0;
while (1)
{
if (iTimeout >= 10)
break;
sleep(1);
iTimeout++;
cout << "Waiting... " << iTimeout << endl;
}
cout << "END" << endl;
exit (0);
}
Using compile command lines:
Solaris:
CC -m64 -g temp.cpp -D_POSIX_PTHREAD_SEMANTICS -lpthread
HP-UX:
/opt/aCC/bin/aCC +p +DA2.0W -AA -g -z -lpthread -mt -I/usr/include temp.cpp
Running both applications, the behaviour (pressing CTRL+C while in the 10 seconds loop):
HP-UX:
./a.out
SigWait...
Waiting... 1
Waiting... 2
Signal!! : 2 <---- CTRL + C
OUT
Waiting... 3
Waiting... 4 <---- CTRL + C again to terminate
Solaris:
./a.out
SigWait...
Waiting... 1
Waiting... 2 <---- CTRL + C
^C
Any help will be more then welcome since I'm already tearing my hair (not much left) :)!
Thanks!
It's unspecified which of your 2 threads will handle SIGINT. If you need only one of your threads to handle the signal, you need to block that signal in all the other threads you have.
You should block signals to other threads by using pthread_sigmask. that page also contains an example for a program with a signal handling thread.
About the only way how to handle signals well in multithreaded application is to do the following:
Block all signals in main() early, before any other threads are spawned, using pthread_sigmask().
Spawn a signals handling thread. Use sigwait() or sigwaitinfo() to handle the signals in a simple loop.
This way no threads except the one dedicated for signal handling will get the signals. Also, since the signal delivery is synchronous this way, you can use any inter-thread communication facilities you have, unlike inside classic signal handlers.
This is rather unorthodox way to handle signals. If you want to marry the signals and threads, better choice would be to have the usual signal handlers from where the signal is serialized internally to another thread which is responsible for the actual handling of the event.
That is also a better option, as it is undefined which thread in an MT application receives the signal. Any threads which doesn't have the signal blocked might receive it. If you have 2 threads (and you have two threads in the example) then any of the threads might get the SIGINT.
You might want to check sigprocmask() as a way to tell OS that SIGINT should be blocked in a thread. That should be done for every thread, IIRC even the one calling sigwait().
Edit1. Actually I'm wrong about the "should be done for every thread" bit above. A new thread inherits its signal mask from the current thread. I have realized that that can't be true because that would have introduced the race condition: signal arrives at the time when new thread created but hasn't yet set its signal mask. In other words, it is sufficient to set the signal mask in the main thread.