[thread] Do we can a non backward compatible version with non-blocking futures?
Hi, in Boost 1.58 I fixed some issues as the result of the async and future<T>:then() was not a blocking future as the documentation described. However the implementation is horrible and I suspect that it is subject to deadlocks. BTW, I added already some fixes on the develop branch. Sometime ago I started in a branch an implementation that don't use blocking futures [1]. Well the implementation is also horrible after making futures non-blocking, but less ;-) The problem is that now I have some issues with the executors :( Anyway, Gor has told me that with the blocking implementation he is unable to define await_suspend (using resumable functions)
It is just if .then returns a blocking future, I cannot subscribe a callback to .then in await_suspend. await_suspend(f, cb) { f.then(cb); } &will block here preventing coroutine from suspending
I don't know yet if he will reach to configure boost::future<T> on the non-blocking branch. My question is if the Boost community will accept (or even wants) a breaking change on a near release (when I would reach to fix the executors issues) including non-blocking futures instead of the current blocking futures. Best, Vicente [1] https://github.com/boostorg/thread/tree/feature/non_blocking_futures
On 05/19/2015 01:58 AM, Vicente J. Botet Escriba wrote:
My question is if the Boost community will accept (or even wants) a breaking change on a near release (when I would reach to fix the executors issues) including non-blocking futures instead of the current blocking futures.
It is unclear to me exactly what the breaking changes are. Are they limited to .then()?
On Thu, May 21, 2015 at 9:06 AM, Bjorn Reese
On 05/19/2015 01:58 AM, Vicente J. Botet Escriba wrote:
My question is if the Boost community will accept (or even wants) a
breaking change on a near release (when I would reach to fix the executors issues) including non-blocking futures instead of the current blocking futures.
It is unclear to me exactly what the breaking changes are. Are they limited to .then()?
boost::future::then() and boost::async() would no longer block in the destructor of the returned future if the worker thread has not yet-completed. I read through the proposals for std::async, and it appears the standards committee made a blocking destructor mainly to reduce potential bugs. It prevents a thread from continuing to execute after main, and prevents exception unsafe code: const auto work = boost::async(boost::launch::async, [&]() { ....}); ... do stuff - an exception could make captured variables go out of scope ... work.get(); Until recently, boost::future::then didn't properly block in the destructor of the returned future anyway. The 1.58 release may have already changed behavior in some code bases if the documentation for boost::future::then was not read. A non-blocking future::then() is convenient for cases like the one Vicente described, but can the executor framework simulate the same behavior? Lee
Le 21/05/15 17:11, Lee Clagett a écrit :
On Thu, May 21, 2015 at 9:06 AM, Bjorn Reese
wrote: On 05/19/2015 01:58 AM, Vicente J. Botet Escriba wrote:
My question is if the Boost community will accept (or even wants) a
breaking change on a near release (when I would reach to fix the executors issues) including non-blocking futures instead of the current blocking futures.
It is unclear to me exactly what the breaking changes are. Are they limited to .then()?
boost::future::then() and boost::async() would no longer block in the destructor of the returned future if the worker thread has not yet-completed. I read through the proposals for std::async, and it appears the standards committee made a blocking destructor mainly to reduce potential bugs. It prevents a thread from continuing to execute after main, and prevents exception unsafe code:
const auto work = boost::async(boost::launch::async, [&]() { ....}); ... do stuff - an exception could make captured variables go out of scope ... work.get();
Until recently, boost::future::then didn't properly block in the destructor of the returned future anyway. The 1.58 release may have already changed behavior in some code bases if the documentation for boost::future::then was not read. Yes 1.58 implemented exactly what was documented. block on destructor.
A non-blocking future::then() is convenient for cases like the one Vicente described, but can the executor framework simulate the same behavior?
Please , could you elaborate? Vicente
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 21/05/15 17:11, Lee Clagett a écrit :
A non-blocking future::then() is convenient for cases like the one Vicente described, but can the executor framework simulate the same behavior?
Please , could you elaborate?
boost::async and boost::future::then seem error prone due to ownership of the execution handle. If the destructor of the boost::future automatically releases ownership, the execution handle cannot be managed. If an executor was provided to boost::async and boost::future::then, the lifetime of the executor could be controlled separately from the returned future. It should prevent threads after main, which never seem like a good idea. Unfortunately, if client code throws before a boost::future::get() then stack references can still be invalidated with the executor (see my prior post) - but there are a number of ways to achieve this incorrect behavior anyway. If boost::future::~future detaches the execution handle, its still possible to create an RAII wrapper that blocks if that behavior is desired. I don't think the opposite is possible, so the change is more flexible. Lee
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 21/05/15 17:11, Lee Clagett a écrit :
A non-blocking future::then() is convenient for cases like the one Vicente described, but can the executor framework simulate the same behavior?
Please , could you elaborate?
boost::async and boost::future::then seem error prone due to ownership of the execution handle. Are talking here of blocking or non-blocking futures? If the destructor of the boost::future automatically releases ownership, the execution handle cannot be managed. Why? If an executor was provided to boost::async and boost::future::then, the lifetime of the executor could be controlled separately from the returned future. I have a branch on which the executors are lightweight. In this branch,
Le 23/05/15 19:05, Lee Clagett a écrit : the future created using asyn/then executor have a reference to the executor. I will create a branch with the non-blocking futures and the lightweight executors.
It should prevent threads after main, which never seem like a good idea. Sorry, I don't understand, it should prevent what? Unfortunately, if client code throws before a boost::future::get() then stack references can still be invalidated with the executor (see my prior post) - but there are a number of ways to achieve this incorrect behavior anyway.
If boost::future::~future detaches the execution handle, its still possible to create an RAII wrapper that blocks if that behavior is desired. I don't think the opposite is possible, so the change is more flexible.
Could you tell us more? Thanks for all your comments, Vicente
On Sun, May 24, 2015 at 7:35 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 23/05/15 19:05, Lee Clagett a écrit :
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 21/05/15 17:11, Lee Clagett a écrit :
A non-blocking future::then() is convenient for cases like the one
Vicente described, but can the executor framework simulate the same behavior?
Please , could you elaborate?
boost::async and boost::future::then seem error prone due to ownership of the execution handle.
Are talking here of blocking or non-blocking futures?
I was referring to non-blocking futures.
If the destructor of the boost::future automatically
releases ownership, the execution handle cannot be managed.
Why?
It was incorrect of me to say that the execution handle cannot be managed (see below). I thought the separate executor argument to boost::async could make it more obvious that the executor object would control the lifetime of the execution thread, and that it could prevent possible data races. I doubt it will make data races to stack references easier to identify, but it should help prevent threads after main, since the executor object could force all execution threads to stop on its destruction instead. It wasn't obvious previously, but I was wondering whether the detach on future destruction should only be enabled when boost::async takes an executor overload.
It should
prevent threads after main, which never seem like a good idea.
Sorry, I don't understand, it should prevent what?
Static destruction could be complicated if there were multiple execution threads after main. That was another issue brought up about not blocking on destruction for the std async/future versions.
Unfortunately, if client code throws before a boost::future::get() then
stack references can still be invalidated with the executor (see my prior post) - but there are a number of ways to achieve this incorrect behavior anyway.
If boost::future::~future detaches the execution handle, its still possible to create an RAII wrapper that blocks if that behavior is desired. I don't think the opposite is possible, so the change is more flexible.
Could you tell us more?
Inspired by ThreadRAII: template<typename T> class FutureRAII { public: FutureRAII(boost::future<T>&& future) : future_(std::move(future)) { } ~FutureRAII() { try { if (future_.valid()) { future_.wait(); } } catch (...) { } } boost::future<T>& future() { return future_; } private: boost::future<T> future_; }; The usual complications of calling a function after a move can appear if future_ was the source of a move through the accessor. Lee
Le 26/05/15 23:43, Lee Clagett a écrit :
On Sun, May 24, 2015 at 7:35 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 23/05/15 19:05, Lee Clagett a écrit :
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
A non-blocking future::then() is convenient for cases like the one
Vicente described, but can the executor framework simulate the same behavior?
Please , could you elaborate? boost::async and boost::future::then seem error prone due to ownership of
Le 21/05/15 17:11, Lee Clagett a écrit : the execution handle.
Are talking here of blocking or non-blocking futures?
I was referring to non-blocking futures.
If the destructor of the boost::future automatically
releases ownership, the execution handle cannot be managed.
Why?
It was incorrect of me to say that the execution handle cannot be managed (see below). I thought the separate executor argument to boost::async could make it more obvious that the executor object would control the lifetime of the execution thread, and that it could prevent possible data races. I doubt it will make data races to stack references easier to identify, but it should help prevent threads after main, since the executor object could force all execution threads to stop on its destruction instead. It wasn't obvious previously, but I was wondering whether the detach on future destruction should only be enabled when boost::async takes an executor overload.
It should
prevent threads after main, which never seem like a good idea.
Sorry, I don't understand, it should prevent what?
Static destruction could be complicated if there were multiple execution threads after main. That was another issue brought up about not blocking on destruction for the std async/future versions. So, you are saying that async() should return a blocking future if there is no executor parameters, and a non-blocking when the executor is given, isn't it?
Unfortunately, if client code throws before a boost::future::get() then
stack references can still be invalidated with the executor (see my prior post) - but there are a number of ways to achieve this incorrect behavior anyway.
If boost::future::~future detaches the execution handle, its still possible to create an RAII wrapper that blocks if that behavior is desired. I don't think the opposite is possible, so the change is more flexible.
Could you tell us more?
Inspired by ThreadRAII:
template<typename T> class FutureRAII { public: FutureRAII(boost::future<T>&& future) : future_(std::move(future)) { }
~FutureRAII() { try { if (future_.valid()) { future_.wait(); } } catch (...) { } }
boost::future<T>& future() { return future_; }
private: boost::future<T> future_; };
The usual complications of calling a function after a move can appear if future_ was the source of a move through the accessor.
Ok, I understand what you meant. Call it blocking_future. This class has its own merit, but it could have also more future like functions. Thanks for your comments, Vicente
On Wed, May 27, 2015 at 1:57 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 26/05/15 23:43, Lee Clagett a écrit :
On Sun, May 24, 2015 at 7:35 AM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 23/05/15 19:05, Lee Clagett a écrit :
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 21/05/15 17:11, Lee Clagett a écrit :
A non-blocking future::then() is convenient for cases like the one
Vicente described, but can the executor framework simulate the same behavior?
Please , could you elaborate?
boost::async and boost::future::then seem error prone due to ownership of the execution handle.
Are talking here of blocking or non-blocking futures?
I was referring to non-blocking futures.
If the destructor of the boost::future automatically
releases ownership, the execution handle cannot be managed.
Why?
It was incorrect of me to say that the execution handle cannot be
managed (see below). I thought the separate executor argument to boost::async could make it more obvious that the executor object would control the lifetime of the execution thread, and that it could prevent possible data races. I doubt it will make data races to stack references easier to identify, but it should help prevent threads after main, since the executor object could force all execution threads to stop on its destruction instead. It wasn't obvious previously, but I was wondering whether the detach on future destruction should only be enabled when boost::async takes an executor overload.
It should
prevent threads after main, which never seem like a good idea.
Sorry, I don't understand, it should prevent what?
Static destruction could be complicated if there were multiple execution
threads after main. That was another issue brought up about not blocking on destruction for the std async/future versions.
So, you are saying that async() should return a blocking future if there is no executor parameters, and a non-blocking when the executor is given, isn't it?
Yes, that is what I am saying. My thinking was that the behavior for the corresponding std functions were chosen as the least worst alternative, and that boost should follow that behavior. When an executor is provided as an argument, the situation is slightly different, and the problems that led to the blocking decision _might_ go away. The behavior differences between overloads might provide more confusion though. Lee
Le 27/05/15 18:55, Lee Clagett a écrit :
On Wed, May 27, 2015 at 1:57 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 26/05/15 23:43, Lee Clagett a écrit :
On Sun, May 24, 2015 at 7:35 AM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 23/05/15 19:05, Lee Clagett a écrit :
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 21/05/15 17:11, Lee Clagett a écrit :
A non-blocking future::then() is convenient for cases like the one
> Vicente > described, but can the executor framework simulate the same behavior? > > Please , could you elaborate? > boost::async and boost::future::then seem error prone due to ownership of the execution handle.
Are talking here of blocking or non-blocking futures? I was referring to non-blocking futures.
releases ownership, the execution handle cannot be managed.
Why? It was incorrect of me to say that the execution handle cannot be managed (see below). I thought the separate executor argument to boost::async could make it more obvious that the executor object would control the lifetime of
If the destructor of the boost::future automatically the execution thread, and that it could prevent possible data races. I doubt it will make data races to stack references easier to identify, but it should help prevent threads after main, since the executor object could force all execution threads to stop on its destruction instead. It wasn't obvious previously, but I was wondering whether the detach on future destruction should only be enabled when boost::async takes an executor overload.
prevent threads after main, which never seem like a good idea.
Sorry, I don't understand, it should prevent what? Static destruction could be complicated if there were multiple execution
It should threads after main. That was another issue brought up about not blocking on destruction for the std async/future versions.
So, you are saying that async() should return a blocking future if there is no executor parameters, and a non-blocking when the executor is given, isn't it?
Yes, that is what I am saying. My thinking was that the behavior for the corresponding std functions were chosen as the least worst alternative, and that boost should follow that behavior. When an executor is provided as an argument, the situation is slightly different, and the problems that led to the blocking decision _might_ go away. The behavior differences between overloads might provide more confusion though.
From your and Gor comments I should preserve the async blocking future behavior when no executor is given, deprecated the interface taking an executor and let the the function taking the executor return a future non-blocking at destruction. I would need a new name for this function, what do you prefer, submit/spawn/post? From Gor comments, then() must return a non-blocking future, that will have all the troubles you are mentioning, so there will be not coherency. I believe that misunderstood the blocking future feature from the begining. Currently only the destructor of the last future pointing to the shared state block. Before this it was the destructor of the shared state that blocked. The RAII you suggested will block on the destructor of all the futures (which is much more simple). Is this what people expect from a blocking future. The current blocking implementation is too error prone. I would prefer to not support it anymore. I will see if I can move to a blocking future that blocks on destructor if valid as your RAII. Should the shared futures coming from a blocking future block on destruction as well? Vicente
On Thu, May 28, 2015 at 1:53 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 27/05/15 18:55, Lee Clagett a écrit :
On Wed, May 27, 2015 at 1:57 AM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 26/05/15 23:43, Lee Clagett a écrit :
On Sun, May 24, 2015 at 7:35 AM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 23/05/15 19:05, Lee Clagett a écrit :
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 21/05/15 17:11, Lee Clagett a écrit :
A non-blocking future::then() is convenient for cases like the one > > Vicente >> described, but can the executor framework simulate the same >> behavior? >> >> Please , could you elaborate? >> >> boost::async and boost::future::then seem error prone due to > ownership > of the execution handle.
Are talking here of blocking or non-blocking futures?
I was referring to non-blocking futures.
If the destructor of the boost::future automatically
releases ownership, the execution handle cannot be managed.
Why?
It was incorrect of me to say that the execution handle cannot be
managed (see below). I thought the separate executor argument to boost::async could make it more obvious that the executor object would control the lifetime of the execution thread, and that it could prevent possible data races. I doubt it will make data races to stack references easier to identify, but it should help prevent threads after main, since the executor object could force all execution threads to stop on its destruction instead. It wasn't obvious previously, but I was wondering whether the detach on future destruction should only be enabled when boost::async takes an executor overload.
It should
prevent threads after main, which never seem like a good idea.
Sorry, I don't understand, it should prevent what?
Static destruction could be complicated if there were multiple execution
threads after main. That was another issue brought up about not blocking on destruction for the std async/future versions.
So, you are saying that async() should return a blocking future if there is no executor parameters, and a non-blocking when the executor is given, isn't it?
Yes, that is what I am saying. My thinking was that the behavior for the
corresponding std functions were chosen as the least worst alternative, and that boost should follow that behavior. When an executor is provided as an argument, the situation is slightly different, and the problems that led to the blocking decision _might_ go away. The behavior differences between overloads might provide more confusion though.
From your and Gor comments I should preserve the async blocking future behavior when no executor is given, deprecated the interface taking an executor and let the the function taking the executor return a future non-blocking at destruction. I would need a new name for this function, what do you prefer, submit/spawn/post?
post or dispatch seems appropriate.
From Gor comments, then() must return a non-blocking future, that will have all the troubles you are mentioning, so there will be not coherency.
I believe that misunderstood the blocking future feature from the begining. Currently only the destructor of the last future pointing to the shared state block. Before this it was the destructor of the shared state that blocked. The RAII you suggested will block on the destructor of all the futures (which is much more simple). Is this what people expect from a blocking future.
Assuming boost::async behavior doesn't change - I don't think of the blocking_future class as a future. I think of it as a utility to guarantee work completion in a specific scoping (just like future did previously with boost::async). The use case would be preventing reference(s) held by a callback from going bad, or guaranteeing that work completed in a specific scope (even with exceptions). So I wouldn't suggest a shared version of this. If shared_future was needed, and the callback contained references, you'd likely group a blocking future with the destruction of the object being referenced. So, I see two reasons to block: (1) the computed result of the future is needed, or (2) preventing undefined/racy behavior. If there are multiple points that need to be blocked for (2), you need to block/wait when any of the points are reached, just like with (1). So blocking on last shared_future really only prevents a runaway thread after main (which is only needed in the non-executor design). Ideally code wouldn't even need to be concerned about (2), because the callback has pure semantics. Things don't always work out that way (unfortunately).
Should the shared futures coming from a blocking future block on destruction as well?
Are you asking about changing the behavior to make the shared future blocking instead of the shared state? That way the first shared future to destruct must wait for work completion? I don't see a reason to change that behavior for boost::async, and I didn't expect blocking_future to be convertible to a shared_future of any kind (I didn't see it as a future). Lee
Le 30/05/15 16:55, Lee Clagett a écrit :
On Thu, May 28, 2015 at 1:53 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 27/05/15 18:55, Lee Clagett a écrit :
On Wed, May 27, 2015 at 1:57 AM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
On Sun, May 24, 2015 at 7:35 AM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Le 23/05/15 19:05, Lee Clagett a écrit :
On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba <
> vicente.botet@wanadoo.fr> wrote: > > Le 21/05/15 17:11, Lee Clagett a écrit : > > A non-blocking future::then() is convenient for cases like the one >> Vicente >>> described, but can the executor framework simulate the same >>> behavior? >>> >>> Please , could you elaborate? >>> >>> boost::async and boost::future::then seem error prone due to >> ownership >> > of > the execution handle. > > Are talking here of blocking or non-blocking futures? > I was referring to non-blocking futures.
If the destructor of the boost::future automatically
releases ownership, the execution handle cannot be managed. > Why? > It was incorrect of me to say that the execution handle cannot be
managed (see below). I thought the separate executor argument to boost::async could make it more obvious that the executor object would control the lifetime of the execution thread, and that it could prevent possible data races. I doubt it will make data races to stack references easier to identify, but it should help prevent threads after main, since the executor object could force all execution threads to stop on its destruction instead. It wasn't obvious previously, but I was wondering whether the detach on future destruction should only be enabled when boost::async takes an executor overload.
It should
prevent threads after main, which never seem like a good idea. > Sorry, I don't understand, it should prevent what? > Static destruction could be complicated if there were multiple execution
threads after main. That was another issue brought up about not blocking on destruction for the std async/future versions.
So, you are saying that async() should return a blocking future if there is no executor parameters, and a non-blocking when the executor is given, isn't it?
Yes, that is what I am saying. My thinking was that the behavior for the corresponding std functions were chosen as the least worst alternative, and
Le 26/05/15 23:43, Lee Clagett a écrit : that boost should follow that behavior. When an executor is provided as an argument, the situation is slightly different, and the problems that led to the blocking decision _might_ go away. The behavior differences between overloads might provide more confusion though.
From your and Gor comments I should preserve the async blocking future behavior when no executor is given, deprecated the interface taking an executor and let the the function taking the executor return a future non-blocking at destruction. I would need a new name for this function, what do you prefer, submit/spawn/post?
post or dispatch seems appropriate. What is wrong with submit?
From Gor comments, then() must return a non-blocking future, that will have
all the troubles you are mentioning, so there will be not coherency.
I believe that misunderstood the blocking future feature from the begining. Currently only the destructor of the last future pointing to the shared state block. Before this it was the destructor of the shared state that blocked. The RAII you suggested will block on the destructor of all the futures (which is much more simple). Is this what people expect from a blocking future.
Assuming boost::async behavior doesn't change - I don't think of the blocking_future class as a future. I think of it as a utility to guarantee work completion in a specific scoping (just like future did previously with boost::async). The use case would be preventing reference(s) held by a callback from going bad, or guaranteeing that work completed in a specific scope (even with exceptions). So I wouldn't suggest a shared version of this. If shared_future was needed, and the callback contained references, you'd likely group a blocking future with the destruction of the object being referenced. So, I see two reasons to block: (1) the computed result of the future is needed, If the user needs it she must call get(), isn't it? or (2) preventing undefined/racy behavior. If there are multiple points that need to be blocked for (2), you need to block/wait when any of the points are reached, just like with (1). So blocking on last shared_future really only prevents a runaway thread after main (which is only needed in the non-executor design). Yes, I will move again to the blocking on the destructor of the shared state to follow the standard. Ideally code wouldn't even need to be concerned about (2), because the callback has pure semantics. Things don't always work out that way (unfortunately).
Should the shared futures coming from a blocking future block on destruction as well?
Are you asking about changing the behavior to make the shared future blocking instead of the shared state? No. Sorry for the imprecision. That way the first shared future to destruct must wait for work completion? No, the last one. I don't see a reason to change that behavior for boost::async, and I didn't expect blocking_future to be convertible to a shared_future of any kind (I didn't see it as a future).
Sorry for confusion, but I believe that we agree. I have no blocking_future. I have just future and shared_future and a function share() that moves from one to the other. The standard says [ Note: If a future obtained from std::async is moved outside the local scope, other code that uses the future must be aware that the future’s destructor may block for the shared state to become ready. — end note ] C++ International Standard Note that it is not the destructor of the future that could block, buy the destructor of the shared state. I misunderstood this point some versions ago (1.57 or 1.58) Best, Vicente BTW, the develop version has removed the blocking futures completely. To reestablish the async blocking behavior uncomment line 17 of future.hpp //#define BOOST_THREAD_FUTURE_BLOCKING I've yet some issues with unwrap :(
Le 21/05/15 15:06, Bjorn Reese a écrit :
On 05/19/2015 01:58 AM, Vicente J. Botet Escriba wrote:
My question is if the Boost community will accept (or even wants) a breaking change on a near release (when I would reach to fix the executors issues) including non-blocking futures instead of the current blocking futures.
It is unclear to me exactly what the breaking changes are. Are they limited to .then()?
No. In the non-blocking futures async return also a non-blocking future. I could change this if we think it is better to preserve async behavior. Vicente
On 05/21/2015 11:08 PM, Vicente J. Botet Escriba wrote:
No. In the non-blocking futures async return also a non-blocking future. I could change this if we think it is better to preserve async behavior.
It is probably better to have both then() and async() behave in the same way. Anyways, I have no objections to the changes you are proposing.
participants (3)
-
Bjorn Reese
-
Lee Clagett
-
Vicente J. Botet Escriba