My thoughts on this:
We should provide explicit cancellation on promises, but promise::cancel()
should work as the abstract equivalent of promise::require_anonymous(INFINITY)
(which obviously cannot be directly expressed in code, but this is the most concise expression of my proposed semantics). Under this formulation:
-
Cancellation is an explicit operation and never invoked implicitly.
-
Promise cancellation does not cancel any in-flight operations that might be holding refs (which is good, because that's not something we're able to efficiently/robustly support). However it places the
promise
into a state where it's guaranteed to never reach fulfillment (dep count 0), regardless of the number of subsequentpromise::fulfill*()
calls (which are still permitted, as are all other promise operations). As such, futures referencing this header are guaranteed to never become ready. -
As a corollary to the above,
promise::cancel()
would have a precondition ofdependency_count >= 1
, for the same reason aspromise::require_anonymous()
. IOW it's an error to request cancellation of a promise that has already reached fulfillment, because that's a meaningless request. -
There is no cancellation on
future
objects, cancellation requires the caller have access to thepromise
object (for all the same reasons we don't have afuture::require_anonymous()
) -
We could mandate (and check in debug mode) that dropping all refs to promise requires either fulfillment (dep count == 0) or cancellation (dep count == INFINITY). I think this is sufficient to fix the leak.
This still leaves an open question about whether we allow future::then()
on a future whose promise has been cancelled. I think we could go either way on this, although if we choose to prohibit it then I think we need to also provide a cancellation query to help the user dynamically avoid the prohibition. If we follow this path, then I think both the prohibition and query probably needs to be transitive to dependent futures - eg:
promise<> p;
future<> f1 = p.get_future();
future<> f2 = f1.then(...); // ok
p.cancel();
assert(f1.is_cancelled()); // the directly cancelled header
f1.then(...); // this would be an error
assert(f2.is_cancelled()); // dependent on a cancelled header
f2.then(...); // this would also be an error
My thoughts on this:
We should provide explicit cancellation on promises, but
promise::cancel()
should work as the abstract equivalent ofpromise::require_anonymous(INFINITY)
(which obviously cannot be directly expressed in code, but this is the most concise expression of my proposed semantics). Under this formulation:Cancellation is an explicit operation and never invoked implicitly.
Promise cancellation does not cancel any in-flight operations that might be holding refs (which is good, because that's not something we're able to efficiently/robustly support). However it places the
promise
into a state where it's guaranteed to never reach fulfillment (dep count 0), regardless of the number of subsequentpromise::fulfill*()
calls (which are still permitted, as are all other promise operations). As such, futures referencing this header are guaranteed to never become ready.As a corollary to the above,
promise::cancel()
would have a precondition ofdependency_count >= 1
, for the same reason aspromise::require_anonymous()
. IOW it's an error to request cancellation of a promise that has already reached fulfillment, because that's a meaningless request.There is no cancellation on
future
objects, cancellation requires the caller have access to thepromise
object (for all the same reasons we don't have afuture::require_anonymous()
)We could mandate (and check in debug mode) that dropping all refs to promise requires either fulfillment (dep count == 0) or cancellation (dep count == INFINITY). I think this is sufficient to fix the leak.
This still leaves an open question about whether we allow
future::then()
on a future whose promise has been cancelled. I think we could go either way on this, although if we choose to prohibit it then I think we need to also provide a cancellation query to help the user dynamically avoid the prohibition. If we follow this path, then I think both the prohibition and query probably needs to be transitive to dependent futures - eg: