Boost logo

Boost :

Subject: Re: [boost] [rpc] Introducing Boost.Reflect, Boost.CMT, and Boost.RPC
From: Daniel Larimer (dlarimer_at_[hidden])
Date: 2011-07-29 23:08:03


On Jul 29, 2011, at 7:39 PM, Jose wrote:

>> I would like to know if I am barking up the right tree here and if anyone
>> has any feedback. If someone has a real project they would like to see this
>> code incorporated into I would be more than willing to focus my development
>> effort toward those needs.
>
> The three libraries look really useful. The documentation is clear but
> a bit sparse.
>
There is so much to cover. I never got around to discussing how CMT does
priority scheduling or too much detail into how real multi-threading works.

> RPC lib:
> - Can you compare yours to other widely used libraries, e.g.Thrift
>
1) No code generation required (best for C++ to C++, but with a proper JSON interface can communicate with any language)
        - eliminate extra build steps
        - changing your code or structs in one spot applies every where.
        - expose any existing code as an RPC service in a non-intrusive manner.
2) Asynchronous operations performed via futures
        - Boost.CMT allows simple single-threaded programs to appear
                 multi-threaded.
        - Benefits of async, with ease of use of synchronous code.
        - No need to have a real OS thread per 'async operation' nor block an OS thread waiting on a result.
3) Clean syntax, minimal macro usage
4) Automatic stubs, complete with full type erasure provided by boost::reflect::any_ptr<>
        - expose the same interface over JSON-RPC/TCP/UDP/HTTP, XML-RPC, protocol buffers, boost::serialization, etc
        - your algorithms interface with boost::reflect::any_ptr<> and so do not care if they are dealing with
                an RPC stub a local object, or a local actor running in a separate thread.
5) boost::signals over RPC

6) Asynchronous RPC call:

        Assuming you are using the mirror delegate then everything looks synchronous, but can be made asynchronous using boost::cmt
        
        boost::any_ptr<Interface> client_ptr;
        boost::cmt::async<ReturnType>( boost::bind(client_ptr->method_name, arg1, arg2 ) ).wait( timeout );
        
        I have not yet implemented it, but boost::cmt::actor_interface will adapt all of the methods on an interface to return a future<> instead
        of the real return value.

Thus:
        boost::any_ptr<Interface,boost::cmt::actor_interface> client_ptr;
        future<ReturnType> r = client_ptr->method_name( arg1, arg2 );

        And of course, future<ReturnType> automatically casts to ReturnType and blocks (yields) if necessary. Then I would have a
boost::actor_ptr<Interface> that could cast to and from a boost::any_ptr<Interface>.

        I am torn about making boost::cmt depend upon boost::reflect vs introducing yet another library called boost::actor and then having
boost::rpc depend upon boost::actor.

> CMT lib:
> - Can you compare your library to the use of coroutines in the
> Boostcon 2011 example
> https://github.com/chriskohlhoff/awesome

Awesome appears to provide stackless coroutines whereas CMT uses Boost.Context to provide a real stack per fiber.

> - Could you provide a more real-life example, e.g. a proxy like in the
> above example ?

I use 'coroutines/fibers/futures' any time I need to perform an asynchronous operation and then it turns into a
"synchronous" operation where all 'in-flight' tasks are multi-plexed on one real thread. When I actually want
to do things in 'parallel' I use boost::cmt::async() and get the return value as a future. So I could enqueue 10 tasks, then
wait for 10 results. I often spawn a long-running task that exits with a signal/flag and I 'join' it by waiting on its future.

Other things I do is have one fiber 'wait' on a boost::signal for a value from another fiber.

Lastly, any time I need to do inter-thread communication, I get the 'cmt::thread' and schedule an async task. The result is that
all of my code is thread-safe and lock-free as long as the shared resource is only accessed from one thread with other threads delegating
queries to it. (Much like boost::asio::strand keeps things 'thread safe').

The only thing I need to be careful about is long-running tasks that do not yield. They can block all other tasks in a particular thread. With boost CMT it is easy enough to spawn these tasks off into a real thread. I try to avoid any blocking system calls as they stall not only the current fiber, but all other tasks assigned to that thread.

> - Can you provide an example that shows how to handle timeouts ?

cmt::future<ReturnType> f = boost::cmt::async<ReturnType>( Functor, priority );
try {
        ReturnType r = f.wait( timeout_us);
} catch( const boost::cmt::future_wait_timeout& e ) {
       .. handle timeout here....
}

> thanks
> jose
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk