boost::process::child will not exit after closing input stream - c++

In the following example I try to write some data to a child process, which processes the data and writes it to a file. After closing the stream the parent process waits indefinitely for the child to finish. I am at a loss to know how to indicate that I’m done writing the data and would like the child process to stop reading and finish whatever it is doing. According to the documentation calling terminate would send a SIGKILL which I don’t think is what I want.
What am I missing? I checked this question but I would rather try to make the actual code work with synchronous IO first.
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main(int argc, char **argv)
{
boost::process::opstream in{};
boost::process::child child("/path/to/test.py", bp::std_in < in);
in << "test1\n";
in << "test2\n";
in << "test3\n";
in << std::flush;
std::cerr << "Closing the stream…\n";
in.close();
std::cerr << "Waiting for the child to exit…\n";
child.wait(); // Parent seems to hang here.
return 0;
}
test.py just writes the data to a file like so:
#!/usr/local/homebrew/opt/python#3.8/bin/python3
import sys
with open("/tmp/test.txt", "w") as f:
for line in sys.stdin:
f.write(line)

After inspecting the source code, I found out that closing the stream did not close the associated pipe at least in this case. Doing that manually did solve the issue:
...
in.close();
in.pipe().close();
child.wait(); // Does not hang.

The documentation warns that using synchronous IO to child processes is prone to deadlock.
Here's a minimal reword to async IO:
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main() {
boost::asio::io_context ioc;
bp::async_pipe in{ioc};
bp::child child("./test.py", bp::std_in < in, bp::std_out.close());
for (auto msg : { "test1\n", "test2\n", "test3\n" }) {
write(in, bp::buffer(msg, strlen(msg)));
}
std::cerr << "Closing the pipe…\n";
in.close();
std::cerr << "Waiting for the child to exit…\n";
ioc.run(); // already awaits completion
child.wait(); // Parent seems to hang here.
}
You can make it more realistic by doing some delays:
#include <boost/process.hpp>
#include <iostream>
using namespace std::chrono_literals;
namespace bp = boost::process;
int main() {
boost::asio::io_context ioc;
bp::async_pipe in{ioc};
bp::child child("./test.py", bp::std_in < in, bp::std_out.close());
std::thread th([&] {
for (auto msg : { "test1\n", "test2\n", "test3\n" }) {
write(in, bp::buffer(msg, strlen(msg)));
std::this_thread::sleep_for(1s);
}
std::cerr << "Closing the pipe…\n";
in.close();
});
std::cerr << "Waiting for the child to exit…\n";
ioc.run(); // already awaits completion
th.join();
child.wait(); // Parent seems to hang here.
}
For fullblown async IO see other examples:
simultaneous read and write to child's stdio using boost.process
How to retrieve program output as soon as it printed?
Running a process using boost process in async mode with timeout

Related

Using boost::interprocess::named_mutex correctly

I'm having problems using named_mutex, which I am trying to use to determine if another instance of my application is running.
I defined a global variable:
named_mutex dssMutex{ open_or_create, "DeepSkyStacker.Mutex.UniqueID.12354687" };
In main() I then wrote:
if (!dssMutex.try_lock()) firstInstance = false;
and at the end of main() after all the catch stuff I did:
dssMutex.unlock();
The problem I have encountered is that try_lock() is returning false when this is the only instance of my program in the system (just after a reboot). I also see this in the debug log (which may just be an artefact of try_lock()):
Exception thrown at 0x00007FFB838C4FD9 in DeepSkyStacker.exe: Microsoft C++ exception: boost::interprocess::interprocess_exception at memory location 0x00007FF5FFF7EF00.
So what am I doing wrong?
Thanks
David
Three things:
you should not unlock if try_lock returned false;
you should be exception safe, which is easier with the scoped_lock helper
Boost's interprocess locking primitives are not robust mutexes. This means that if your process gets hard-terminated without unlocking, the lock will be stuck. To the best of my knowledge the implementation(s) on Windows contain a "boot time" field which serves to recover the lock after a reboot, though, so your described scenario should really not be a problem.
The Exception
The exception shown should not be a problem unless it goes unhandled. If you're using Visual Studio you can configure the debugger to break on exceptions thrown or unhandled. The best explanation for the message is that it is handled internally. The worst explanation is that you're not handling it. In that case it will explain that the lock is not released.
Note that the exception might be cause by trying to unlock after failing to try_lock?
Code Sample
Here's how I'd use a deffered scope-lock to achieve exception safety:
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <iostream>
#include <thread>
namespace bip = boost::interprocess;
using namespace std::chrono_literals;
int main(int, char** argv) {
bip::named_mutex dssMutex{bip::open_or_create, "UniqueID.12354687"};
bip::scoped_lock<bip::named_mutex> lk(dssMutex, bip::defer_lock);
bool const firstInstance = lk.try_lock();
std::cout << argv[0] << (firstInstance?" FRIST!":" SECOND") << std::flush;
std::this_thread::sleep_for(1s);
std::cout << " Bye\n" << std::flush;
}
Coliru cannot handle it but here's what that does locally:
Signal Handling
Now, as mentioned, this is still not robust, but you can make it less bad by at least handling e.g. SIGINT (what happens on POSIX when you Ctrl-C in the terminal):
#include <boost/asio.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <iostream>
#include <thread>
namespace bip = boost::interprocess;
using namespace std::chrono_literals;
int main(int, char** argv) {
boost::asio::thread_pool ioc(1);
boost::asio::signal_set ss(ioc, SIGINT, SIGTERM);
ss.async_wait([](auto ec, int s) {
if (ec == boost::asio::error::operation_aborted)
return;
std::cerr << "signal " << s << " (" << ec.message() << ")" << std::endl;
});
bip::named_mutex dssMutex{bip::open_or_create, "UniqueID.12354687"};
bip::scoped_lock<bip::named_mutex> lk(dssMutex, bip::defer_lock);
bool const firstInstance = lk.try_lock();
std::cout << argv[0] << (firstInstance?" FRIST!":" SECOND") << std::flush;
std::this_thread::sleep_for(1s);
std::cout << " Bye\n" << std::flush;
ss.cancel();
ioc.join();
}
Now it's okay to interrupt the processes:
for a in {1..10}; do sleep "0.$RANDOM"; ./one; done&
for a in {1..10}; do sleep "0.$RANDOM"; ./two; done&
sleep 3; pkill -INT -f ./one;
sleep 5; pkill -INT -f ./two
If you look closely, the handler doesn't actually do anything now. So likely you want to make sure it cleanly shuts down main.

UDP Communication using Boost (for MATLAB s-function)

I'm trying to create an s-function (using C++ Boost library) for UDP communication.
Implementing the sender was fairly straightforward, 15 min job. I'm struggling to get the receiver to work.
I created the following in Visual Studio:
#define _WIN32_WINNT 0x0501
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <stdio.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;
using boost::asio::ip::udp;
using namespace std;
std::vector<char> receive_buffer;
void process_received_frame(const boost::system::error_code& error, size_t received_frame_size) {
if (error) {
cout << "Receive failed: " << error.message() << "\n";
return;
}
size_t ByteCount = 0;
std::cout << endl << "Received byte stream (Handler) [" << received_frame_size << "]: ";
for (std::vector<char>::const_iterator iter = receive_buffer.cbegin(); iter != receive_buffer.cend(); iter++)
{
ByteCount++;
printf("%02X ", (UINT8)*iter);
if (ByteCount == received_frame_size)
{
break;
}
}
std::cout << endl;
}
int main(int argc, char *argv[])
{
boost::asio::io_service io_service;
udp::socket socket(io_service);
udp::endpoint remote_endpoint = udp::endpoint(boost::asio::ip::address_v4::from_string("127.0.0.1"), 19001);
socket.open(udp::v4());
socket.bind(udp::endpoint(remote_endpoint));
receive_buffer.resize(255);
try
{
socket.async_receive_from(boost::asio::buffer(receive_buffer),
remote_endpoint,
boost::bind(&process_received_frame, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
catch (const std::exception& exp)
{
printf("%s\n", exp.what());
}
//io_service.poll();
io_service.run();
cout << "End";
std::cin.ignore();
}
I tried sending UDP to localhost:19001 from Simulink and was able to receive the UDP packets in Visual Studio. The handler (process_received_frame) gets called and everything seems to work, as expected.
But, given that, io_service::run() works in blocking mode, it pauses execution if there is nothing received on port 19001. So I tried using io_service::poll() (commented in the code above) instead. However, when I use poll(), it does not execute the handler. If I try to display the contents of 'receive_buffer' from main(), I get all 0s. Interestingly, when I single-step through the code for accessing the elements of 'receive_buffer' I do get the right values.
Not sure what is it that I'm doing wrong. Quite likely to be a school-boy-error.
When I convert this to an s-function for MATLAB-Simulink, it does the same thing - all zeros.
Any help would be much appreciated.
Cheers,
In your handler function, you need to call socket.async_receive_from at the end after processing the answer. io_service.run() returns when no more handler are in its processing queue.
See the example from boost doc here: udp sync server example
EDIT
Rereading your question/comment, I'm not sure what your expected output or behavior is.
If you're only expecting a single UDP frame, then maybe call io_service.run_one().
If you don't want run() to block your main thread, you need to launch another thread to call run(). Something like:
boost::asio::io_service io_service;
// Process handlers in a background thread.
boost::thread t(boost::bind(&io_service::run, &io_service));
...
io_service::run() is always a blocking call. Completion handlers can only be called from threads currently calling run(). The only time run() is going to return is when there is no more handlers in the queue (you stopped calling async_receive) or if you explicitly cancel the run() command by calling stop() or reset()

Can we create unnamed socket with boost.asio to emulate anonymous pipe?

socketpair() on Linux lets you create unnamed socket. Is something similar in boost.asio library possible? I'm trying to emulate anonymous pipe with boost.asio library. I know boost.process supports this but I want to use boost.asio library. By-the-way why is anonymous pipe missing from boost.asio?
I wrote the code below to emulate pipe using boost.asio library. Its only demo code and there is no message boundary checking, error checking etc.
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
#include <cctype>
#include <boost/array.hpp>
using boost::asio::local::stream_protocol;
int main()
{
try
{
boost::asio::io_service io_service;
stream_protocol::socket parentSocket(io_service);
stream_protocol::socket childSocket(io_service);
//create socket pair
boost::asio::local::connect_pair(childSocket, parentSocket);
std::string request("Dad I am your child, hello!");
std::string dadRequest("Hello son!");
//Create child process
pid_t pid = fork();
if( pid < 0 ){
std::cerr << "fork() erred\n";
} else if (pid == 0 ) { //child process
parentSocket.close(); // no need of parents socket handle, childSocket is bidirectional stream socket unlike pipe that has different handles for read and write
boost::asio::write(childSocket, boost::asio::buffer(request)); //Send data to the parent
std::vector<char> dadResponse(dadRequest.size(),0);
boost::asio::read(childSocket, boost::asio::buffer(dadResponse)); //Wait for parents response
std::cout << "Dads response: ";
std::cout.write(&dadResponse[0], dadResponse.size());
std::cout << std::endl;
} else { //parent
childSocket.close(); //Close childSocket here use one bidirectional socket
std::vector<char> reply(request.size());
boost::asio::read(parentSocket, boost::asio::buffer(reply)); //Wait for child process to send message
std::cout << "Child message: ";
std::cout.write(&reply[0], request.size());
std::cout << std::endl;
sleep(5); //Add 5 seconds delay before sending response to parent
boost::asio::write(parentSocket, boost::asio::buffer(dadRequest)); //Send child process response
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
std::exit(1);
}
}

Boost.Asio - polling a named pipe

I am trying to listen for input on a named pipe. I'm using Boost.Asio's stream_descriptor and async_read under Linux. The problem is, the call to io_service::run() only blocks like I want it to until the first read. After that, it just keeps calling the handler immediately with the "End of file" error, even though I try to attach more async_reads to it. The code I have is equivalent to the following:
boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true)
{
// buffer and handler probably aren't important for the purposes of this question
boost::asio::async_read(fifo, buffer, handler);
io_service.run();
}
Only the first async_read works as I expect it to. Subsequent async_reads just return immediately. The only way I found to make it work like I want is to close and reopen the named pipe, but it seems like a hack:
boost::asio::io_service io_service;
while (true)
{
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
boost::asio::async_read(fifo, buffer, handler);
io_service.run();
close(fifo_d);
}
Can anyone tell me what am I doing wrong?
UPDATE: Here's a simple "read" version, which allowed for some code simplification, the problem remains the same:
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
while (true) {
try {
boost::asio::read(fifo, boost::asio::buffer(buffer));
}
catch (boost::system::system_error& err) {
// It loops here with "read: End of file" error
std::cout << err.what() << std::endl;
}
}
This is not how works. run() is not intended to be called in a loop. If you insist, you need to call reset() in between (as per the documentation).
Also, if you /want/ blocking behaviour, why are you using the async_* interface?
Demos
Consider using a simple iostream to read the fd:
Live On Coliru
#include <iostream>
#include <fstream>
int main() {
std::ifstream fifo("/tmp/fifo");
std::string word;
size_t lineno = 0;
while (fifo >> word) {
std::cout << "word: " << ++lineno << "\t" << word << "\n";
}
}
Or if you must attach to some fd you get from somewhere else, use file_descriptor from Boost IOstreams:
Live On Coliru
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>
#include <fcntl.h>
int main() {
namespace io = boost::iostreams;
using src = io::file_descriptor_source;
io::stream<src> fifo(src(open("./fifo", O_RDONLY), io::file_descriptor_flags::close_handle));
std::string word;
size_t number = 0;
while (fifo >> word) {
std::cout << "word: " << ++number << "\t" << word << "\n";
}
}
Both examples print the expected:
word: 1 hello
word: 2 world
As also sehe reported, that's not the way boost::asio works.
The ioservice::run() method runs in blocking mode while it has some work. When the ioservice goes out of work you have to call the reset() method before putting other work, so that's why in your first code the async_read is done only once.
A common pattern in this case would look something like:
void handler(...) {
if (!error) {
// do your work
boost::asio::async_read(fifo, buffer, handler); // <-- at the end of the handler a subsequent async_read is put to the ioservice, so it never goes out-of-work
}
}
boost::asio::io_service io_service;
int fifo_d = open("/tmp/fifo", O_RDONLY);
boost::asio::posix::stream_descriptor fifo(io_service, fifo_d);
boost::asio::async_read(fifo, buffer, handler); // <-- you call async_read only once here.
io_service.run(); //<-- this blocks till an error occurs

boost::asio signal_set handler only executes after first signal is caught and ignores consecutive signals of the same type

I have a program and would like to stop it by sending SIGINT for writing some data to a file instead of exiting immediately. However, if the user of the program sends SIGINT again, then the program should quit immediately and forget about writing data to a file.
For portability reason I would like to use boost::asio for this purpose.
My initial (simplified) approach (see below) did not work. Is this not possible or am I missing something?
The handler seems to be called only once (printing out the message) and the program always stops when the loop has reached the max iteration number.
void handler(
const boost::system::error_code& error,
int signal_number) {
if (!error) {
static bool first = true;
if(first) {
std::cout << " A signal(SIGINT) occurred." << std::endl;
// do something like writing data to a file
first = false;
}
else {
std::cout << " A signal(SIGINT) occurred, exiting...." << std::endl;
exit(0);
}
}
}
int main() {
// Construct a signal set registered for process termination.
boost::asio::io_service io;
boost::asio::signal_set signals(io, SIGINT);
// Start an asynchronous wait for one of the signals to occur.
signals.async_wait(handler);
io.run();
size_t i;
for(i=0;i<std::numeric_limits<size_t>::max();++i){
// time stepping loop, do some computations
}
std::cout << i << std::endl;
return 0;
}
When your first event is handled, you don't post any new work on the service object, so it exits.
This means that then (after the ioservice exited) the tight loop is started. This may not be what you expected.
If you want to listen for SIGINT again, you have to wait for the signal set again from the handler:
#include <boost/asio.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/bind.hpp>
#include <boost/atomic.hpp>
#include <iostream>
void handler(boost::asio::signal_set& this_, boost::system::error_code error, int signal_number) {
if (!error) {
static boost::atomic_bool first(true);
if(first) {
// do something like writing data to a file
std::cout << " A signal(SIGINT) occurred." << std::endl;
first = false;
this_.async_wait(boost::bind(handler, boost::ref(this_), _1, _2));
}
else {
std::cout << " A second signal(SIGINT) occurred, exiting...." << std::endl;
exit(1);
}
}
}
int main() {
// Construct a signal set registered for process termination.
boost::asio::io_service io;
boost::asio::signal_set signals(io, SIGINT);
// Start an asynchronous wait for one of the signals to occur.
signals.async_wait(boost::bind(handler, boost::ref(signals), _1, _2));
io.run();
return 2;
}
As you can see I bound the signal_set& reference to the handler in order to be able to async_wait on it after receiving the first signal. Also, as a matter of principle, I made first an atomic (although that's not necessary until you run the io_service on multiple threads).
Did you actually wish to run the io_service in the background? In that case, make it look like so:
signals.async_wait(boost::bind(handler, boost::ref(signals), _1, _2));
boost::thread(boost::bind(&boost::asio::io_service::run, boost::ref(io))).detach();
while (true)
{
std::cout << "Some work on the main thread...\n";
boost::this_thread::sleep_for(boost::chrono::seconds(1));
}
With typical output:
Some work on the main thread...
Some work on the main thread...
Some work on the main thread...
^CSome work on the main thread...
A signal(SIGINT) occurred.
Some work on the main thread...
Some work on the main thread...
^CSome work on the main thread...
A second signal(SIGINT) occurred, exiting....