Process library with child process events

Hi, On Wed, Jun 1, 2011 at 14:12, Jeff Flinn <Jeffrey.Flinn@gmail.com> wrote:
Gregory Crosswhite wrote:
Hey everyone,
Since there is not yet a Boost.Process library, does anyone have a suggestion for a lightweight cross-platform C++ library for launching and monitoring (i.e. being able to tell if they die) child processes?
I've put the code from the recent boostcon "boost process" presentation that Boris Schaeling and I gave at:
https://github.com/JeffFlinn/boost-process
Initial documentation is at:
https://docs.google.com/document/pub?id=1qdpxcJB51WMuNYlKYPkPcZZGuPj7zmeUPIp...
The intent is to get this completed and ask for review this summer. The areas needing work are:
I've read the current (clearly incomplete w.i.p.) doc and parsed the headers and I like the design so far. However, I can't find a way to be notified if a child process did end, by getting to the end of the program or by having crashed or by having being shut down by external processes, the OS or the user. Did I miss something? If not, what is supposed to happen in the parent process if a child do get to an end (whatever the cause)? Joël Lamotte

Klaim - Joël Lamotte wrote:
Hi,
On Wed, Jun 1, 2011 at 14:12, Jeff Flinn <Jeffrey.Flinn@gmail.com> wrote:
Gregory Crosswhite wrote:
Hey everyone,
Since there is not yet a Boost.Process library, does anyone have a suggestion for a lightweight cross-platform C++ library for launching and monitoring (i.e. being able to tell if they die) child processes?
I've put the code from the recent boostcon "boost process" presentation that Boris Schaeling and I gave at:
https://github.com/JeffFlinn/boost-process
Initial documentation is at:
https://docs.google.com/document/pub?id=1qdpxcJB51WMuNYlKYPkPcZZGuPj7zmeUPIp...
The intent is to get this completed and ask for review this summer. The areas needing work are:
I've read the current (clearly incomplete w.i.p.) doc and parsed the
Very w.i.p. :-(
headers and I like the design so far.
Thanks.
However, I can't find a way to be notified if a child process did end, by getting to the end of the program or by having crashed or by having being shut down by external processes, the OS or the user.
Did I miss something? If not, what is supposed to happen in the parent process if a child do get to an end (whatever the cause)?
This area needs more work, there is just enough there to support our current needs. The boost::process::monitor::join method is used to wait for a child process to 'finish' due to whatever cause, which returns the exit code on windows and the status on posix. There is a template method boost::process::monitor::join<class EXCEPTION> that throws a default constructed exception of the supplied type when join returns a non-zero value. Can you provide a more concrete set of requirements? The previous iteration(s) of process had an asynch wait facility that may be able to be used with the latest design. Boris, do you have any ideas here? Jeff

On Thu, Jun 2, 2011 at 14:57, Jeff Flinn <Jeffrey.Flinn@gmail.com> wrote:
This area needs more work, there is just enough there to support our current needs. The boost::process::monitor::join method is used to wait for a child process to 'finish' due to whatever cause, which returns the exit code on windows and the status on posix.
There is a template method boost::process::monitor::join<class EXCEPTION> that throws a default constructed exception of the supplied type when join returns a non-zero value.
Yes, I've seen the join functions but that's clearly not enough. What if the parent process does have things to do too while the child process works? It looks like using join would then require a listener thread for each child process that I want to get notified the end of. Is it a reasonable way of handling it? It looks like a bit overkill to have a thread+process pair for each child process... But maybe there is no other way, I'm not sure because I don't know a lot about the problem domain.
Can you provide a more concrete set of requirements?
For my personal case ( a game with client/server organisation ) : One main process that is also a client application. It launches child processes that are server applications. The main process simply need to be notified if a child process, a server, did end. There are, if we don't get into details, two cases: 1. The process ended correctly : the server ended because of predefined causes that have been communicated to the parent process. 2. The process ended incorrectly : an error made the process crash, the process have been killed by something external, etc. I need be notified about both cases for each child process to react in a consistent manner for the user of the client application. It would also allow me to help catching inter-process problems like a bug killing the child process without affecting the parent process that does require the child process to be alive to run correctly. So the main problem is 2., because it requires the child process to send informations just before crashing or for the parent process to have a way to detect such crashes from outside (some kind of ping?). I don't know how to implement this last suggestion.
The previous iteration(s) of process had an asynch wait facility that may be able to be used with the latest design. Boris, do you have any ideas here?
I did give my 2cents about it, It was missing something I don't remember exactly and/or it was using the thread+process pair solution. Joël Lamotte

Klaim - Joël Lamotte wrote:
On Thu, Jun 2, 2011 at 14:57, Jeff Flinn <Jeffrey.Flinn@gmail.com> wrote:
This area needs more work, there is just enough there to support our current needs. The boost::process::monitor::join method is used to wait for a child process to 'finish' due to whatever cause, which returns the exit code on windows and the status on posix.
There is a template method boost::process::monitor::join<class EXCEPTION> that throws a default constructed exception of the supplied type when join returns a non-zero value.
Yes, I've seen the join functions but that's clearly not enough. What if the parent process does have things to do too while the child process works? It looks like using join would then require a listener thread for each child process that I want to get notified the end of. Is it a reasonable way of handling it? It looks like a bit overkill to have a thread+process pair for each child process... But maybe there is no other way, I'm not sure because I don't know a lot about the problem domain.
Can you provide a more concrete set of requirements?
For my personal case ( a game with client/server organisation ) :
One main process that is also a client application. It launches child processes that are server applications. The main process simply need to be notified if a child process, a server, did end. There are, if we don't get into details, two cases:
1. The process ended correctly : the server ended because of predefined causes that have been communicated to the parent process. 2. The process ended incorrectly : an error made the process crash, the process have been killed by something external, etc.
I need be notified about both cases for each child process to react in a consistent manner for the user of the client application. It would also allow me to help catching inter-process problems like a bug killing the child process without affecting the parent process that does require the child process to be alive to run correctly.
So the main problem is 2., because it requires the child process to send informations just before crashing or for the parent process to have a way to detect such crashes from outside (some kind of ping?). I don't know how to implement this last suggestion
Are both the parent/child process application code yours? We accomplish something similar by using asio with async socket communication between the parent and child processes effectively sending an exit code. If the either dies the socket closure manifests as an asio failure. We have a single thread with an asio io_service run. But that single thread can handle multiple connections to multiple child processes.
The previous iteration(s) of process had an asynch wait facility that may be able to be used with the latest design. Boris, do you have any ideas here?
I did give my 2cents about it, It was missing something I don't remember exactly and/or it was using the thread+process pair solution.
IIRC, the async wait of the previous process library would effectively work the same way, without needing socket communication, and a single thread with the io_service:run call to handle multiple child communication. As a game you most likely have a gui application. At boostcon Chris Kolhoff suggested that you could call, IIRC, io_service::poll from your event loop to avoid a second thread. Jeff

On Fri, Jun 3, 2011 at 04:29, Jeff Flinn <Jeffrey.Flinn@gmail.com> wrote:
Are both the parent/child process application code yours?
Yes.
We accomplish something similar by using asio with async socket communication between the parent and child processes effectively sending an exit code. If the either dies the socket closure manifests as an asio failure. We have a single thread with an asio io_service run. But that single thread can handle multiple connections to multiple child processes.
The project is currently paused but that was what I began to implement, using another library for networking (RakNet). Not sure if it provide an equivalent but if it don't I can still use boost::asio just to do this. So if you tell me it's a safe enough way to handle it using this approach (I don't feel confident in the communication domain so I'm never sure...yet), as I have the source code of both processes, I guess I'll continue on this way.
IIRC, the async wait of the previous process library would effectively work the same way, without needing socket communication, and a single thread with the io_service:run call to handle multiple child communication.
As a game you most likely have a gui application. At boostcon Chris Kolhoff suggested that you could call, IIRC, io_service::poll from your event loop to avoid a second thread.
That's a good idea, I'll see if I can use it that way. Joël Lamotte

On 06/02/2011 04:31 PM, Klaim - Joël Lamotte wrote:
On Thu, Jun 2, 2011 at 14:57, Jeff Flinn<Jeffrey.Flinn@gmail.com> wrote:
This area needs more work, there is just enough there to support our current needs. The boost::process::monitor::join method is used to wait for a child process to 'finish' due to whatever cause, which returns the exit code on windows and the status on posix.
There is a template method boost::process::monitor::join<class EXCEPTION> that throws a default constructed exception of the supplied type when join returns a non-zero value.
Yes, I've seen the join functions but that's clearly not enough. What if the parent process does have things to do too while the child process works? It looks like using join would then require a listener thread for each child process that I want to get notified the end of. Is it a reasonable way of handling it? It looks like a bit overkill to have a thread+process pair for each child process... But maybe there is no other way, I'm not sure because I don't know a lot about the problem domain.
This issue is solvable, but it is not easy because it can only be solved by first writing a library to handle certain global state such as SIGCHLD, and this is additionally complicated by the fact that the necessary abstractions are not platform independent. Also, on Windows at least one separate thread might be needed in some cases. Nonetheless, I think solving it is of critical importance for a Boost Process library.

On Fri, Jun 3, 2011 at 06:11, Jeremy Maitin-Shepard <jeremy@jeremyms.com>wrote:
This issue is solvable, but it is not easy because it can only be solved by first writing a library to handle certain global state such as SIGCHLD, and this is additionally complicated by the fact that the necessary abstractions are not platform independent. Also, on Windows at least one separate thread might be needed in some cases.
Nonetheless, I think solving it is of critical importance for a Boost Process library.
I believe the 1 thread or regular polling function using asio or similar library can solve my own case, as pointed Jeff, but not the general case where you only have binaries of the other projects and want to be notified. So I agree that it's a very important feature. I'm not experienced enough in the boost community to know if it should be available from the beginning. Joël Lamotte

On 06/03/2011 06:11 AM, Jeremy Maitin-Shepard wrote:
This issue is solvable, but it is not easy because it can only be solved by first writing a library to handle certain global state such as SIGCHLD,
It may have become a little easier, since the latest version of asio supports signals: http://think-async.com/Asio/boost_asio_1_5_3/doc/html/boost_asio.html http://thread.gmane.org/gmane.comp.lib.boost.asio.user/4621/focus=4627

On Wed, 01 Jun 2011 23:45:29 +0200, Klaim - Joël Lamotte <mjklaim@gmail.com> wrote:
[...]I've read the current (clearly incomplete w.i.p.) doc and parsed the headers and I like the design so far. However, I can't find a way to be notified if a child process did end, by getting to the end of the program or by having crashed or by having being shut down by external processes, the OS or the user.
I'm a bit late with my response. But I just finished a first (early) version of what may become a new Windows service for Boost.Asio. The service is based on WaitForMultipleObjects() which can be used (among others) to be notified when a child processes terminates. As WaitForMultipleObjects() can be used for many more things (see the remarks at http://msdn.microsoft.com/en-us/library/ms687025(v=vs.85).aspx), I try to create a more general service which is not bound to Boost.Process. (I agree with Danny that on Unix boost::asio::signal_set from Boost.Asio 1.5.3 should be used.) If you like to give it a try - here's a Visual C++ 2010 project: http://www.highscore.de/boost/AsioObjectHandle.zip The ZIP file is pretty big (3 MB) as I use Google Test and Google Mock and have included those libraries. But then you only need to have Boost 1.46.1 installed (at least that's what I use). As the VC++ project expects to find Boost headers and libs in C:\Boost\include and C:\Boost\lib, you may need to update the project configuration. If you like to see first how the extension is used: ----- void YOUR_HANDLER(boost::system::error_code ec) {} boost::asio::io_service io_service; boost::asio::windows::object_handle obj_handle(io_service, YOUR_WINDOWS_HANDLE); obj_handle.async_wait(YOUR_HANDLER); io_service.run(); ----- The new I/O object object_handle complements random_access_handle and stream_handle. While the latter two are used to read and write (they exist already in Boost.Asio), object_handle is used to wait for a signaled state. The only asynchronous operation supported is async_wait(). Please note that the service is neither complete (eg. cancel() hasn't been implemented yet) nor documented nor sufficiently tested. The more people play around with it, the faster it will be complete though. :) I'll send an email to the asio mailing list in the coming days to discuss the implementation. Boris

I think that It is too complicated. Probally I need a more simple layer. Something that i can use: std:vector<string> arg; p = process('/usr/bin/vi', args=('bob',)); p.start(); p.join(); Something that has an object process_pool. A pipe object between processes, a queue object a signal handler and nothing else. -- Quiero ser el rayo de sol que cada día te despierta para hacerte respirar y vivir en me. "Favola -Moda".

Giorgio Zoppi wrote:
I think that It is too complicated. Probally I need a more simple layer. Something that i can use:
std:vector<string> arg; p = process('/usr/bin/vi', args=('bob',)); p.start(); p.join();
The current design allows you to do the above, where make_child creates and starts a child process. A monitor allows you to join with a child_process. using namespace boost::process; child c = make_child(paths(“/usr/bin/vi”), args(“bob”)); monitor(c).join();
Something that has an object process_pool.
Can you expand on what you mean by process pool? I've run into a use case where I'd like to launch several processes(one per core) and join all. Is that along the lines of what you were thinking? I do this now in client code but will try to get this formalized and added to the library.
A pipe object between processes, a queue object a signal handler and nothing else.
The library provides a 'directed' pipe, which is currently named boost::process::file_descriptor_ray(yep, in search of a better name). It allows communication between parent and/or child processes. One use is to access a child's std_out via a stream by the parent. For example I needed to grab the output of an exe to parse it's version information: namespace io = boost::iostreams; typedef io::stream<io::file_descriptor_source> source; file_descriptor_ray ray; monitor m(make_child(paths(exe), Args(arg), std_out_to(ray))); ray.m_sink.close(); // currently req's manual closing source redirected(ray.m_source); Tuple version(parse(redirected)); // parse the istream m.join(); Jeff

Something that has an object process_pool.
Can you expand on what you mean by process pool? I've run into a use case where I'd like to launch several processes(one per core) and join all. Is that along the lines of what you were thinking? I do this now in client code but will try to get this formalized and added to the library.
Well something like the algorthmic skeleton master-slave is useful. See this: http://en.wikipedia.org/wiki/Algorithmic_skeleton. Over your process library you might implement some skeletons for use at best. I need something of higher level. I dont want to care everytime to join each process. Something like: int four( 4 ), five( 5 ); pool_process_sample sample(3,out_queue); // three processes. sample.set_function( boost::bind( add, four, five ) ) ); sample.start(); sample.join_all() Here i can use the results on the output queue. This scheme can be extended to have a map-reduce, or an input queue as you like. In this case it is difficult have syncronization errors, because it is all hidden. Probally i missed some parts. You could, if you want provide a lower level and an higher level. I will help you to test performance with my work 12 cores workstation :). Cheers, Giorgio.
participants (6)
-
Boris Schaeling
-
Danny Havenith
-
Giorgio Zoppi
-
Jeff Flinn
-
Jeremy Maitin-Shepard
-
Klaim - Joël Lamotte