...At a minimum, kindly state:
- Whether you believe the library should be accepted into Boost
* Conditions for acceptance
YES it should be accepted, though documentation need improvements.
- Your knowledge of the problem domain
I implemented naive and less naives ways to manage different processes in
the past.
I currently work on libraries which are all about multi-process
communication
and we have a poor-man's process management (launching, tracking, etc.) api
that I have to maintain
on Linux, Windows and MacOSX.
I also have home projects which are organized as client-server with
master-slave relationship,
clients spawning servers and pinging them to keep processes alive mutually
(using network communication).
So basically, I implemented basic process management libraries, but I'm
more a process
management library user, in all my projects, paid or not.
I have been exposed to a lot of problems related and solved a lot of them.
I also used the last 2 Boost.Process versions proposed to Boost (though I
voted no for these ones)
but in cases where I just needed a cross-platform way to launch an
arbitrary executable (no
tracking nor communication).
You are strongly encouraged to also provide additional information:
- What is your evaluation of the library's:
* Design
The weird choice of variadic arguments in launching functions have been
explained
so I'm more at ease with it, though maybe ways to simplify it could be
attemted in the future.
I like that the design of boost::process::child follow, where it make
sense, the design of std::thread.
Both could be considered as execution resource, so it's fine to me that
they share
similar interface phylosophies of design (like spawning on construction and
move-only).
I would love it if there was a way (even slow) to track processes lifetime
for processes that
I don't have code for. Though it's ok for me if it's a future extension.
I like the minimalism of the scope, though useful extensions could be added
later.
* Implementation
I briefly eyescanned the code, looked more closely at the termination
trigerriung code
because of the discussion we had on termination reqest vs kill.
I don't bother much about performance for this library as launching
processes is rarely
done in a tight high-performance loop.
* Documentation
I did a report of the documentation a few weeks or months ago, I do not
know if
changes have been made since.
Anyway I think the documentation should have a part focused on explaining
the concepts
because even if most programmers knows what a process is, all related
concepts (like what
is the "environnement") are so specific to processes that they are often
ignored as long as
naive usage works.
So as soon as the user need more specific usage, most, I believe, will not
know what word
to use to find the right concept.
I think this library documentation should follow something similar to
Boost.MSM which introduce
to the basic concepts, though without walls of text.
* Tests
I didn't have time to try it yet in real applications, but so far didn't
find
any problem in basic usage.
* Usefulness
It is very useful for almost all my projects.
- Did you attempt to use the library?
YES
If so:
* Which compiler(s)
Visual Studio 2015 for now.
* What was the experience? Any problems?
I only did minimal testing, nothing deep, no problems.
- How much effort did you put into your evaluation of the review?
Several hours diffused through several months.
Joël Lamotte
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
YES
> * Conditions for acceptance
>
The interface used to spawn processes should be reworked.
> - Your knowledge of the problem domain
>
I have implemented some process wrappers many times. I have used the last
release of Boost.Process (0.5 I think?).
> - What is your evaluation of the library's:
> * Design
>
I think the similarity with the boost::thread api is a great choice. The
lifetime of process
is handled by small functors that will be called at specified times. I
think is a very good
design as it allows one to write small "plugins" in order to affect the
setup or the tear down
of a process. The integration with Boost.Asio follows the previous choice
of using boost::thread
as a reference, i.e. the child process is not the resource you read
from/write to, you need
some separate stream objects whose lifetime are tied to the child process:
struct MyProcess
{
bp::async_pipe std_in;
bp::async_pipe std_out;
bp::async_pipe std_err;
bp::child child;
};
I haven't seen a way however to be asynchronously notified when a child
exited, so I'm not
sure how one would handle gracefully multiple async_read/write while the
process could exit
at any time.
The choice of having a nice interface to spawn processes have been
discussed already, but I
still think that the library should aim to have the simplest and lowest
level interface.
IMO, C++ users do not need to have a nice and expressive syntax to spawn
process, I
cannot imagine a code base where many processes are launched at many places
in the
code. In other words, I think that either you rarely need to spawn a
process (a hack for example),
or your job is to spawn processes (a shell, a make clone, ...). In both
cases, you will
spawn processes in very few places in the code.
Moreover, this interface style has an implication on build times, errors
seen at build time
and makes it hard to use in cases where your program needs to spawn
processes based on
runtime decisions. if one decided to implement a shell that supports
redirections, pipes,
environment modification and background jobs, they would certainly need to
make runtime
adjustments to the arguments given to child constructor. As of today, this
has to be
worked around by the user ; I think this should be the primary interface.
A suggestion has been made to have a more idiomatic C++ construction, I
would extend it
with a specific options type:
auto options = bp::options().args("gcc");
if (verbose) options.append_arg("-v");
if (ignore_errors) options.std_err.redirect(bp::null_device);
bp::child child(io_service, std::move(options));
This way you make it impossible to modify the options after the child
process creation.
But that's just an idea :)
* Implementation
>
The implementation is clear, but the support for the aforementioned
constructor interface
"leaks" into the implementation everywhere. As the previous version of
Boost.Process,
the different OS support is nicely separated and each stage of the process
creation is
clearly implemented in its own header.
> * Documentation
>
The asynchronous section feels a bit short given the many possibilities the
interface
gives us.
> * Tests
>
Didn't check
> * Usefulness
>
I think it would be very useful to have something we can rely on instead of
our own
half baked implementations.
> - Did you attempt to use the library? If so:
> * Which compiler(s)
>
g++4.8 using c++11
> * What was the experience? Any problems?
>
I had many problems and decided to fallback on popen().
The first kind of problems were some compile errors. But the author kindly
stated
that I needed the HEAD of boost (which is not an option for me) but I found
a
workaround by not using some niceties of the interface:
https://github.com/klemens-morgenstern/boost-process/issues/20
https://github.com/klemens-morgenstern/boost-process/issues/21
As said previously, for someone not used to boost.fusion, the errors are
hard to
read.
The other problem that made me give up (temporarily) on using Boost.Process
was the fact that asynchronous reads where unreliable when more than one
process
is spawned.
https://github.com/klemens-morgenstern/boost-process/issues/22
This issue has been closed despite the fact that it is not fixed.
- How much effort did you put into your evaluation of the review?
>
I'd say half a day counting this mail and the testing I made.
Cheers,
--
Raphaël Londeix
http://hotgloupi.fr
auto var = bp::make_variant(bp::std_out);
bp::pipe p;
var = p;
bp::child c("foo", var);
> A suggestion has been made to have a more idiomatic C++ construction, I
> would extend it
> with a specific options type:
>
> auto options = bp::options().args("gcc");
> if (verbose) options.append_arg("-v");
> if (ignore_errors) options.std_err.redirect(bp::null_device);
> bp::child child(io_service, std::move(options));
>
> This way you make it impossible to modify the options after the child
> process creation.
> But that's just an idea :)
Thanks for the idea: option objects might be the best solution for that,
and I think the best way would be to allow an easy implementation of
that. Maybe something like that:
auto options = bp::make_options(bp::exe, bp::args, bp::std_err);
options.set_exe("gcc");
if (verbose) options.add_arg("-v");
if (ignore_errors) options.std_err(bp::null);
bp::child c(options);
I'll need to check if I can come up with a syntax for that, but that
would be a way to easily build an option type, without changing anything
underneath. And that would also be extensible, which the other ideas
were not. And if that works, I might even be able to automatically add
the io_service if you do not pass it, hence reducing the pain caused by
the fusion-errors.
auto opt = bp::make_options(bp::std_err & bp::std_out); //requires an
io_service, since it might be async.
opt.get_io_service(); //and there it is.
Now if you combine these two you'd have no problem making your own
child-implementation.
struct my_child
{
asio::io_service ios;
decltype(bp::make_options(bp::exe, bp::args, bp::std_err) opt{ios};
//so it's only one
bp::async_pipe out(ios); //because you already know what that's
going to be.
bp::child c;
void operator()() {c = child(opt, std_out > out); };
};
This is also extensible. I would however not make this the default,
since it incurrs run- and compile-time overhead.
Yep, those are really ugly even if you know fusion. But I've got no idea
how to create a good error message there, if anybody has an idea, let me
know. SFINAE doesn't really help, because that just tells you, that no
matching function could be called. And deep nested static_asserts also
don't help anybody, especially since they get buried in the template
error messages.
>
> The other problem that made me give up (temporarily) on using Boost.Process
> was the fact that asynchronous reads where unreliable when more than one
> process
> is spawned.
>
> https://github.com/klemens-morgenstern/boost-process/issues/22
>
> This issue has been closed despite the fact that it is not fixed.
Well I've got several tests for that, and the particular example (with
echo) also can fail when only used with one - that's why I assume(d)
there's some other issue.
But since we found a design bug during the review, which is actually
caused by a faulty analysis on my part, the behaviour of pipes will
change. That means that the way the async_pipe handles the exit of the
child process will be different and we'll see if that fixes it.
> - How much effort did you put into your evaluation of the review?
> I'd say half a day counting this mail and the testing I made.
>
> Cheers,
>