-
Notifications
You must be signed in to change notification settings - Fork 142
Open
Description
Boost version: 1.72.0
I would like to use boost::process::async_system() within a composed operation (example taken from composed_6.cpp).
The helper boost::asio::async_compose() creates a temporary object (boost::asio::detail::composed_op<>) which contains all necessary variables/states during the whole composed operation. Unfortunately my program (below) doesn't compile. The (lengthy) compiler message looks like boost::process::async_system() tries to make another copy of the completion object (self). But boost::asio::detail::composed_op<> is not copyable.
Is it possible to avoid making an extra copy of the completion object in boost::process::async_system() (like the other boost::asio::async_xxx functions behave)?
regards
Christian
#include <cerrno>
#include <memory>
#include <iostream>
#include <string>
#include <utility>
#include <boost/asio/buffer.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/local/stream_protocol.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/process/args.hpp>
#include <boost/process/async_system.hpp>
#include <boost/process/env.hpp>
#include <boost/process/environment.hpp>
#include <boost/process/exe.hpp>
#include <boost/process/extend.hpp>
#include <boost/process/handles.hpp>
#include <boost/process/io.hpp>
#include <boost/process/posix.hpp>
#include <boost/system/error_code.hpp>
static constexpr char const *gpsdExecutable = "/usr/sbin/gpsd"; // always absolute, also on host
static constexpr char const *gpsdControlSocket = "/run/gpsd.sock"; // always absolute, also on host
static void prepareGpsdSockets(
boost::asio::local::stream_protocol::acceptor &controlSocket,
boost::asio::ip::tcp::acceptor &exportSocket,
boost::asio::ip::tcp::resolver::results_type const &endpoints,
boost::system::error_code &ec)
{
/* control socket */
boost::filesystem::remove(gpsdControlSocket, ec); // ignore errors
boost::asio::local::stream_protocol::endpoint controlSocketEp(gpsdControlSocket);
controlSocket.open(controlSocketEp.protocol(), ec);
if (ec)
return;
controlSocket.bind(controlSocketEp, ec);
if (ec)
return;
controlSocket.listen(boost::asio::socket_base::max_listen_connections, ec);
if (ec)
return;
/* export socket TCPv4 */
boost::asio::ip::tcp::endpoint const &exportSocketEp = *endpoints.begin();
exportSocket.open(exportSocketEp.protocol(), ec);
if (ec)
return;
exportSocket.set_option(boost::asio::socket_base::reuse_address(true), ec);
if (ec)
return;
exportSocket.bind(exportSocketEp, ec);
if (ec)
return;
exportSocket.listen(boost::asio::socket_base::max_listen_connections, ec);
if (ec)
return;
}
template<typename CompletionToken>
inline auto async_startGpsd(boost::asio::io_context &ioContext, CompletionToken &&token)
{
enum class State { starting, resolving, forking };
return boost::asio::async_compose<CompletionToken, void(boost::system::error_code)>(
[
state = State::starting,
&ioContext,
/* Note: std::make_shared() doesn't help here. */
controlSocket = std::make_unique<boost::asio::local::stream_protocol::acceptor>(ioContext),
resolver = std::make_unique<boost::asio::ip::tcp::resolver>(ioContext),
exportSocket = std::make_unique<boost::asio::ip::tcp::acceptor>(ioContext)
]
(
auto &self, // reference to intermediate completion handle (provided by async_compose)
boost::system::error_code ec = {},
boost::asio::ip::tcp::resolver::results_type endpoints = {},
int exitCode = {}
) mutable
{
if (ec)
{
self.complete(ec);
return;
}
switch (state)
{
case State::starting:
{
state = State::resolving;
/* lookup gpsd port number from /etc/services */
resolver->async_resolve(boost::asio::ip::tcp::v4(), "", "gpsd", boost::asio::ip::resolver_base::flags::passive, std::move(self));
return; // Composed operation not yet complete.
}
case State::resolving:
{
state = State::forking;
prepareGpsdSockets(*controlSocket, *exportSocket, endpoints, ec);
if (ec)
break;
auto controlHandle = controlSocket->native_handle();
auto exportHandle = exportSocket->native_handle();
boost::process::async_system(
ioContext,
[self=std::move(self)]
(boost::system::error_code ec, int exitCode)
mutable
{
self(ec, boost::asio::ip::tcp::resolver::results_type(), exitCode);
},
boost::process::exe = gpsdExecutable,
boost::process::args = {
"--listenany",
},
/* the order of bind() and close() is important */
boost::process::std_in < stdin, // don't close std handles
boost::process::std_out > stdout,
boost::process::std_err > stderr,
boost::process::posix::fd.bind(3, controlHandle),
boost::process::posix::fd.bind(4, exportHandle),
boost::process::posix::fd.close(controlHandle),
boost::process::posix::fd.close(exportHandle),
boost::process::limit_handles,
boost::process::extend::on_exec_setup = [](auto &executor)
{
auto env = boost::this_process::environment();
env["LISTEN_PID"] = std::to_string(boost::this_process::native_handle());
env["LISTEN_FDS"] = "2"; // number of sockets
/* use latest ::environ for ::execve() */
executor.env = env.native_handle();
}
);
return; // Composed operation not yet complete.
}
case State::forking:
if (exitCode) // error code from gpsd
{
ec.assign(ESRCH /* No such process */, boost::system::system_category());
}
break; // Composed operation complete, continue below.
}
// Call the user-supplied handler with the result of the operation.
self.complete(ec);
},
token, ioContext);
}
int main(void)
{
boost::asio::io_context ioContext;
async_startGpsd(ioContext,
[](boost::system::error_code const &ec)
{
std::cout << "async_startGpsd() finished with ec = \n" << ec;
});
}
Metadata
Metadata
Assignees
Labels
No labels