On 05/25/2015 07:37 PM, Niall Douglas wrote:
On 25 May 2015 at 18:33, Avi Kivity wrote:
In particular, error_code is fast, and unexpected returns are not exceptional and must be as fast as expected returns. As I mentioned, in this case the user can use expected<> or similar themselves. As I mentioned, expected<> is too hard on compile times for large code bases. It's also way overkill for what 98% of use cases need.
Otherwise, what's the type of error_code? There could be an infinite amount of error_code types to choose from (starting with simple 'enum class'es and continuing with error codes that include more information about the error (nonscalar objects). It's std::error_code. Same as ASIO uses. If being compiled as part of Boost, I expect boost::error_code and boost::exception_ptr will work too as additional variant options.
Also, any monadic transport would default construct to an unexpected state of a null error_code in fact, which is constexpr. This lets one work around a number of exception safety irritations where move constructor of T is not noexcept more easily. I'm not sure how the default constructor of future<> and the move constructor of T are related. Well, let's assume we're really talking about a maybe<T>, and future<T> subclasses maybe<T> with additional thread safety stuff.
In this situation a maybe<T> doesn't need a default constructor, but because it's a fixed variant we always know that error_code is available, and error_code (a) doesn't allocate memory and (b) is STL container friendly, so it seems sensible to make maybe<T> also STL container friendly by letting it default to error_code.
The problem, as with the WG21 proposed variant, is getting move assignment to not undefine behaviour the existing contents if the throwing move constructor throws. Boost Variant's copy constructor dynamically allocates a temporary copy of itself internally to give that strong guarantee - this is unacceptable overhead for mine. So I need some well defined state to default to if during move assignment my move constructor throws after I have destructed my current state. Defaulting to an error_code saying the move constructor threw is a reasonable well defined outcome.
I'm not even sure why future<> would require a default constructor. Seastar's doesn't have one. My future promise is as close to a strict superset of the Concurrency TS as is possible. It should be drop in replaceable in 99% of use cases, with the only point of failure being if you are trying to use allocators with your futures.
My future promise is also intended to enter the Boost.Thread rewrite as the next gen future promise, if it proves popular.
... turns into a "mov $5, %eax", so future<T> is now also a lightweight monadic return transport capable of being directly constructed. I'm looking forward to it! I've been bitten by the same compile time explosion problems and I'm curious to see how you solved them. With a great deal of concentrated study of compiler diagnostics and trial and error!
Once they are working and they are being unit tested per commit, I'll get a CI failure every time I break it. That should make things enormously easier going. Lots of machinery and scripting to come before that though.
I've got everything working except the sequence:
promise<int> p; p.set_value(5); return p.get_future().get();
This should reduce to a mov $5, %eax, but currently does not for an unknown reason. I'm just about to go experiment and see why.
I managed to get very close to this by sprinking always_inline attributes, mostly at destructors. As soon as the compiler makes a bad inlining decision, it loses track of the values it propagated and basically has to undo all optimization. It's still not perfect (one extra instruction) though. (using gcc 5).