shared_ptr, pimpl, reference semantics, and const correctness
I am continuing work on a futures library for boost. future objects (as
currently considered by the C++ standards committee, in particular Peter
Dimov's proposal) have reference semantics, like many pimpl types.
I have some burning questions about const correctness.
Hypothetical reference-semantic example:
future f1;
future f2 = f1; //f2 refers to the same underlying object as f1
f2.set(42); //do something
assert(f1.get() == 42); // see, same underlying object
This is roughly equivalent to:
boost::shared_ptr
Braddock Gaskill wrote:
// alternative #3 - pass by value // Constructing shared_ptr's is VERY inefficient void f3(future f) { bool v = f.get(); // no problem, get() is const f.set(!v); // cannot do this, set() is not const }
The construction of the shared_ptr (if not optimized out when the source is an rvalue) consists of one atomic increment. Its destruction at the end of f costs one atomic decrement and one memory barrier. In contrast, f.get() costs at best one atomic load and one memory barrier, and at worst blocks for an unspecified amount of time. f.set very likely costs one user-kernel transition (and a memory barrier), which is typically much more than a few atomic instructions. This of course doesn't address your const-correctness question, which disappears if you make f.set const. :-) In the next C++ we'll also have void f5( future && f ); as an option.
Hi Peter, On Wed, 01 Aug 2007 20:07:27 +0300, Peter Dimov wrote:
Braddock Gaskill wrote:
// alternative #3 - pass by value // Constructing shared_ptr's is VERY inefficient
The construction of the shared_ptr (if not optimized out when the source is an rvalue) consists of one atomic increment. Its destruction at the end of f costs one atomic decrement and one memory barrier.
I guess "VERY inefficient" is a relative term. I just spent a weak optimizing a DOM library where I attempted to use shared_ptr's in all element references to parse documents with millions of elements. Probably an inappropriate use from the start. As you point out, a future is not a flyweight class, so it may not be as critical.
This of course doesn't address your const-correctness question, which disappears if you make f.set const. :-)
Yes, I am considering that, but it seems semantically crooked. What would you recommend? I'm trying to stay on the same page as your N2185 futures proposal. Your proposal does not define set_value() as const, so users will presumably face the same issues. I can force users to pass by future value, but that doesn't feel like good practice and completely negates any const awareness anyway. Does "&&" remove this concern for N2185? (I'm not really familiar with the upcoming language features). Thanks, Braddock Gaskill
Braddock Gaskill wrote:
This of course doesn't address your const-correctness question, which disappears if you make f.set const. :-)
Yes, I am considering that, but it seems semantically crooked.
What would you recommend? I'm trying to stay on the same page as your N2185 futures proposal. Your proposal does not define set_value() as const, so users will presumably face the same issues.
Very good question. I personally pass shared_ptrs by value all the time without feeling any guilt so passing a future by value in the cases where I want to call non-const methods is good enough for me. On the other hand, is this an issue in a split future/promise model? Are you moving back to N2185-style "combined" future and if so, why? There's a discussion on the committee -lib reflector at the moment which has raised an interesting point: if no futures remain, it might be desirable (in some cases) to cancel the thread that is executing the task as nobody is listening for the result. This is the opposite of watching for the case where no promise is left and breaking the future::waits, and another possible argument in favor of a split future/promise model. The chances of N2185 being accepted in the standard at this point are approximately zero, so there's no need to stick to it except as a sanity check against design regressions. :-)
On Friday 10 August 2007 21:13, Peter Dimov wrote:
There's a discussion on the committee -lib reflector at the moment which has raised an interesting point: if no futures remain, it might be desirable (in some cases) to cancel the thread that is executing the task as nobody is listening for the result.
I personally like being able to "fire and forget" without having to keep the future around to prevent the asyncronous function call from cancelling itself. Especially in the case of a future<void> return value. -- Frank
participants (3)
-
Braddock Gaskill
-
Frank Mori Hess
-
Peter Dimov