Dear community, dear experts!
I am trying to use boost::process to call weasyprint to convert HTML to
a picture.
To do that I wrote a test program (further down in the mail).
This worked fine and did what i expected.
But it stopped working as soon as I tried to convert two files at the
same time by changing
new renderer_t(io_context, "<head></head><body></body>");
in main to
new renderer_t(io_context, "<head></head><body></body>");
new renderer_t(io_context, "<head></head><body></body>");
Now weasyprint hangs.
I changed the call to weasyprint to weasyprint2, which is a short bash
script for testing purposes:
---------------------------------------------------------------------------------
#! /bin/bash
cat > /dev/null
echo "This should be a PNG picture"
---------------------------------------------------------------------------------
It reads stdin until it gets EOF and then writes something to stdout
before terminating.
Same result.
It seems that my program does not close stdin of the child, so it never
gets EOF on stdin.
But only when more than one child process is running at the same time.
So obviously I am doing something wrong, but I have no idea what!
Does anybody have a hint for me?
I am on xubuntu 20.10
compiler is g++ (Ubuntu 10.2.0-13ubuntu1) 10.2.0,
boost version 1.75.0
compiler command line (from my IDE):
g++ -c main.cpp -g -O0 -Wall -Wextra -std=c++17 -o
build-Debug/main.cpp.o -I. -I.
---------------------------------------------------------------------------------
#include
#include
#include
#include
#include <iostream>
namespace bp = boost::process;
bool is_eof(
std::error_code error)
{
boost::system::error_code eof = boost::asio::error::eof;
return error == eof;
}
struct renderer_t : public boost::intrusive_ref_counter {
using pointer = boost::intrusive_ptr;
boost::asio::io_context &io_context;
const std::string html;
bp::child child;
bp::async_pipe in;
bp::async_pipe out;
bp::async_pipe err;
std::array outbuf;
std::array errbuf;
std::vector<unsigned char> result;
std::error_code result_error;
std::string errmsg;
~renderer_t()
{
std::cout << "renderer_t::~renderer_t\n";
if (!errmsg.empty())
std::cerr << "renderer_t::~renderer_t: stderr: " << errmsg << "\n";
}
renderer_t(
boost::asio::io_context &io_context,
const std::string &html) :
io_context(io_context),
html(html),
in(io_context),
out(io_context),
err(io_context)
{
post(
io_context,
[self = pointer(this)]()
{
self->start();
});
}
void start()
{
std::error_code ec;
std::cout << "renderer_t::start\n";
bp::child c(
"weasyprint2 -m screen -p -f png - /dev/stdout",
bp::std_in < in,
bp::std_out > out,
bp::std_err > err,
bp::on_exit=[self = pointer(this)](
int exit,
std::error_code error)
{
self->on_exit(error, exit);
},
bp::start_dir="/data/build/",
io_context,
ec);
if (ec) {
std::cerr << "renderer_t::start: creating child failed: " <<
ec.message() << "\n";
if (!result_error)
result_error = ec;
return;
}
child = std::move(c);
boost::asio::async_write(
in,
boost::asio::buffer(html),
[self = pointer(this)](
std::error_code error,
std::size_t bytes)
{
self->on_write(error, bytes);
});
out.async_read_some(
boost::asio::buffer(outbuf),
[self = pointer(this)](
std::error_code error,
std::size_t bytes)
{
self->on_read_out(error, bytes);
});
err.async_read_some(
boost::asio::buffer(errbuf),
[self = pointer(this)](
std::error_code error,
std::size_t bytes)
{
self->on_read_err(error, bytes);
});
}
void on_write(
std::error_code error,
std::size_t bytes)
{
std::cout << "renderer_t::on_write\n";
if (error) {
std::cerr << "renderer_t::on_write: failed: " << error.message() << "\n";
if (!result_error)
result_error = error;
return;
}
if (bytes != html.size()) {
std::cerr << "renderer_t::on_write: short write\n";
}
boost::system::error_code ec;
in.close(ec);
if (ec) {
std::cerr << "renderer_t::on_write: close failed: " <<
error.message() << "\n";
}
}
void on_read_out(
std::error_code error,
std::size_t bytes)
{
std::cout << "renderer_t::on_read_out\n";
if (is_eof(error)) {
std::cout << "renderer_t::on_read_out: EOF\n";
return;
} else if (error) {
std::cerr << "renderer_t::on_read_out: failed: " << error.message()
<< "\n";
if (!result_error)
result_error = error;
return;
} else {
result.insert(
end(result),
begin(outbuf),
begin(outbuf) + bytes);
out.async_read_some(
boost::asio::buffer(outbuf),
[self = pointer(this)](
std::error_code error,
std::size_t bytes)
{
self->on_read_out(error, bytes);
});
}
}
void on_read_err(
std::error_code error,
std::size_t bytes)
{
std::cout << "renderer_t::on_read_err\n";
if (is_eof(error)) {
std::cout << "renderer_t::on_read_err: EOF\n";
return;
} else if (error) {
std::cerr << "renderer_t::on_read_err: failed: " << error.message()
<< "\n";
if (!result_error)
result_error = error;
return;
} else {
std::string e(begin(errbuf), begin(errbuf) + bytes);
std::cerr << "renderer_t::on_read_err: from stderr: " << e << "\n";
errmsg.insert(
end(errmsg),
begin(errbuf),
begin(errbuf) + bytes);
err.async_read_some(
boost::asio::buffer(errbuf),
[self = pointer(this)](
std::error_code error,
std::size_t bytes)
{
self->on_read_err(error, bytes);
});
}
}
void on_exit(
std::error_code error,
int exit)
{
std::cout << "renderer_t::on_exit\n";
if (error) {
std::cerr << "renderer_t::on_exit: error: " << error.message() << "\n";
if (!result_error)
result_error = error;
return;
}
if (exit) {
std::cerr << "renderer_t::on_exit: weasyprint returned " << exit << "\n";
if (!result_error)
result_error =
make_error_code(std::errc::no_such_process);
}
}
};
int main()
{
boost::asio::io_context io_context;
bp::async_pipe in(io_context);
new renderer_t(io_context, "<head></head><body></body>");
io_context.run();
return 0;
}
---------------------------------------------------------------------------------
Regards
Harald