Boost logo

Boost :

From: Stjepan Rajko (stipe_at_[hidden])
Date: 2007-05-01 18:34:47


Hi Braddock,

Your suggestions on using futures to for return values and "out"
parameters are dead on with what I was hoping for! Also, I like the
prospect of using futures to distinguish between sync and async calls,
rather than providing dual functionality directly in the library...
Details below:

> I personally would suggest looking at how futures can make the async/sync
> elements easier.
>
> Imagine I have a function 'rpc' which calls the passed function and arguments
> remotely and returns a future<T>, where T is the return type.
>
> Along the lines of:
> future<T> rpc(F function, INARG1, ... , INARGN, OUTARG1, ... OUTARGN);
>
> 1) Synchronous behavior:
> cout << "1+2 = " << rpc(add, 1, 2) << "\n";

Great! Now the rpc framework offers only an asynchronous interface,
and the futures are what can make it synchronous... I don't see any
drawbacks to this at the moment, and it makes the rpc code a great
deal simpler.

Also, looks like the same thing would work if the actual function had,
say, void (int &) signature, with the int & being an "out" parameter,
and I wanted to distinguish between sync and async behavior by passing
an int & versus a future<int> & - I think I will need to accept such
arguments in the rpc using a wrapper anyway, so if the user passes
reference to the data type (rather than a reference to the future)
then the reference just gets assigned the value of an internal future
(making the behavior synchronous).

>
> 2) Async behavior:
> future<int> fi = rpc(add, 1, 2);
> ...doing other things...
> cout << "1+2 = " << fi.get() << "\n"; //will block if fi is not yet complete
>

Got that to work.

> 3) rpc call fails:
> future<int> fi = rpc(add, 1, 2);
> try {
> cout << "1+2 = " << fi << "\n"; //will throw if fi contains exception
> } catch (rpc_connect_error &e) {
> ...
> }
>
> 4) I want to cancel the rpc if it isn't ready yet:
> future<int> fi = rpc(add, 1, 2);
> ...doing other things...
> fi.cancel(); // cancels if not yet complete
>

Cancellation by cancelling the feature is a cool idea... I assume the
promise can be notified that all futures have been cancelled via a
callback? Speaking of... if a function call has many outstanding
promises/futures (e.g., a return value and a few "out" arguments), is
there a way to group those all together so that they can be cancelled
as a group?

> 5) I want a basic timeout
> fi.timed_wait(100 /*ms*/);
> if (!fi.ready()) fi.cancel();
>

OK, but I think similar functionality should also be added to the rpc
interface - it might be useful to be able to set a flat timeout for an
individual function (or all functions), and then cancel the promise in
case of a timeout. What might be useful in this respect, is the
ability to tell a future (at the time it is assigned to a promise) to
auto-cancel itself in X time (or, perhaps, to tell the promise the
same thing).

> 6) My remote call may return an exception (and my rpc library has some
> exception transportation mechanism)
> try {
> cout << "1+2 = " << fi << "\n";
> } catch (...) {
> cout << "ERROR\n";
> }

I do plan to add exception transportation. Is it possible to have the
future throw a custom (specified by the promise) exception? Your
point 3) suggests that this is possible also.

>
> 7) I have both a return value and an "Out" argument:
> future<int> multiplied;
> future<int> fi = rpc(add_and_mult, 1, 2, multiplied);
> cout << "1+2 = " << fi << "\n";
> cout << "1*2 = " << multiplied << "\n";
>

ATM, seems like future doesn't have a default constructor, so
future<int> multiplied; doesn't compile.

Also, if the argument was "InOut" rather than just "Out", we'll need
some mechanism (maybe already exists) that will pass the "in" value
with the future, and then the future can be reassigned to the rpc
promise by rpc. It's either this or forcing the user to pass a pair
for the argument (the current value as well as the future).

> This is the ideal application of futures. My futures library can support all
> of the above uses today without modification. As a bonus, then any code which
> uses futures can transparently use the rpc library as a back-end (except for
> dispatch of course)...a future is a future is a future. I'll work with you as
> I prepare for boost submission if you want to explore this route.
>

Using futures to get the return value I was able to get working pretty
easily - works beautifully, no problems at all. Using them to get
modified values of "out" parameters I can't get working without a
default constructor (unless I stick the futures inside the call
class). To try the rest of the things I need to get the rpc side of
the functionality up to speed.

But yeah, I definitelly want to explore this route! If I catch enough
time, I'll wrap up the next iteration of the library with as much
future-related functionality as I can. What would help me the most is
some documentation of the features - I've been getting a lot from the
code and the test cases, but sometimes the intended use escapes me.

Thanks,

Stjepan


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