
Braddock Gaskill wrote:
On Sat, 10 Mar 2007 01:54:57 +0200, Peter Dimov wrote:
Braddock Gaskill wrote: My current proposal for std::future<R> is available at http://www.pdimov.com/cpp/N2185.html and will be part of the pre-meeting mailing. We'll see how it fares.
Thanks Peter,
I coded up a quick straight-forward implementation of your proposal. I'll make it available as soon as I test it a little. Maybe it can be grafted into the vault implementation to provide composites, etc.
I have one, too; look at the 'Implementability' section for the links.
I have a few questions/comments:
1) void set_exception( exception_ptr p);
This is tricky. I don't really want to pass and throw to the caller an exception pointer because responsibility for deleting the exception pointer will be very difficult to get right. Imagine the case where two objects each hold references to the same future, and so the exception gets re-thrown twice as they both try to access the value. Which one delete's the exception pointer?
Should the future handle deletion responsibilty when the last future<> reference is gone?
What type should the pointer be? We can't assume that all exceptions are even derived from std::exception.
exception_ptr is described in http://www.pdimov.com/cpp/N2179.html and is typically a reference-counted smart pointer. If the proposal for language support doesn't pass, we'll have to live with something along the lines of http://www.pdimov.com/cpp/N2179/exception_ptr.hpp http://www.pdimov.com/cpp/N2179/exception_ptr.cpp as linked from N2185.
2) Are multiple calls to set_value() permitted for a single future object?
Allowed, with the second and subsequent calls ignored.
3) What should 'timespec' be? I'm using boost::threads::xtime
'struct timespec' as described in POSIX <time.h>, basically struct timespec { time_t tv_sec; long tv_nsec; };
4) set_cancel_handle(thread::handle )
This thread handle seems to assume that future<> is being used with a thread-per-invocation, and does not appear to provide a mechanism for canceling a queued invocaction, which is necessary for at least my application. I'd need a generic "callback-on-cancel()".
Yes, you are right that set_cancel_handle is limited to canceling threads. I was short on time since the pre-mailing deadline was yesterday so I went with what I already had implemented. set_cancel_callback would've been more general, but could open the door to deadlocks. I'll probably revise the paper for the meeting to generalize the cancel support to accept an arbitrary callback if I'm satisfied with the semantics of such a design.
5) join()/try_join() vs wait()/ready()
As far as I can tell, you renamed the wait() and ready() to join() and try_join(). In my opinion, these names are far less clear, and again my anti-thread-per-invocation bias doesn't like the implied thread semantics.
Yes, I could go either way on that one. I opted for consistency with the join/try_join/timed_join family of thread functions.
6) operator R() const;
My opinion here, but if I understand this then the semantics of this are far too subtle.
future<int> x = std::fork(myfunc); //200 lines later z = x + y; // this blocks or throws, mystifying young programmers everywhere
Another tradeoff. I think that the implicit conversion here is worth keeping because the parallelized syntax is closer to the traditional sequential one. It's a matter of taste.
Also, what is the meaning of: mysubroutine(x) // Does this block or throw, or just use a future argument?
What is the meaning if mysubroutine is declared: template<T> mysubroutine(T a);
Whether this is a bug or a feature is also a matter of taste. The conversion allows some mysubroutines to work on a future<X> as if it were an X, doing the 'obvious' thing.