Document number: N4445
Date: 2015-04-09
Project: Programming Language C++, Concurrency and Parallelism Study Group
Reply-to: Agustín Bergé agustinberge@gmail.com, Hartmut Kaiser hartmut.kaiser@gmail.com
Overly attached promise
1. Introduction
This paper proposes changes and additions to std::promise
to allow an
efficient use of its shared state.
2. Motivation
A std::promise
explicitly specifies when its shared state is released, and
under certain circumstances it clings to it and keeps it around long after it
is needed. Since releasing the shared state may cause a user-defined
destructor to run, and that would be an observable effect, there is little
wiggle room for implementations to be more efficient under the as-if rule.
Implementors should be allowed to release the shared state early as an
optimization, reclaiming the shared state resources (which might be limited
resources on some systems) as well as the dynamic memory used by it (if any).
std::promise < int > p; p.get_future(); p.set_value(42); // p's shared state no longer needed, sticks around until ~promise() ... std::promise < int >{}.set_value_at_thread_exit(42); // shared state no longer needed, sticks around until thread exit (maybe) ... |
The intention of this paper is to allow (but not require) an implementation to
release a promise
's shared state as early as possible, once both the future
has been acquired and the shared state has been made ready, or once the last
future releases the shared state of a promise satisfied via one of the
*_at_thread_exit
member functions.
Some existing implementations choose to bundle the state of the promise
itself into its shared state. Such implementations would not be able to retain
their state after releasing the shared state, hence the need for explicit ways
to accomplish the same effects. This can be already accomplished today with
the following constructs:
p = std::promise < int >{}; // okayish std::promise < int >( std::move (p)); // better |
The first line effectively resets the promise
, it first constructs a new
shared state and then replaces the current state, abandoning the old one. The
second line effectively releases the promise
, abandoning any shared state.
This paper additionally proposes two new promise
member functions: reset
and release
, with the aforementioned effects. It should be noted that a
reset
member function could potentially recycle the current shared state,
provided it is its sole owner (either the future was never obtained or it has
already released this shared state).
Finally, this paper includes a drive-by fix to future::get
's wording, which
does not specify that the shared state is released.
3. Implementability
This implementation builds on top of any current implementation. It is sufficient to meet this paper's requirements, but is not as efficient as intended by it.
class promise { // everything else as today void reset() { * this = promise{}; } template < class Allocator> void reset( const Allocator& a) { * this = promise{allocator_arg, a}; } void release() noexcept { promise{ std::move (* this )}; } }; |
4. Proposed Wording
This wording is relative to [N4296].
4.1 Shared state early release
Change 30.6.4 [futures.state], shared state, as indicated.
Add the following as a new paragraph after paragraph 11:
Implementations are allowed to execute user-defined destructors for shared state results without releasing the shared state when this cannot be otherwise observed.
4.2 promise::reset
Change 30.6.5 [futures.promise], class template promise
, as indicated.
Add the following at the end of the synopsis:
// modifiers
void
reset();
template
<
class
Allocator>
void
reset(
const
Allocator& a);
Add the following as a new paragraph after paragraph 26:
void
promise::reset();
template
<
class
Allocator>
void
promise::reset(
const
Allocator& a);
- Effects: Abandons any shared state (30.6.4) and then as if
promise().swap(*this)
for the first version and as ifpromise(allocator_arg, a).swap(*this)
for the second version.
4.3 promise::release
Change 30.6.5 [futures.promise], class template promise
, as indicated.
Add the following at the end of the synopsis:
// modifiers
void
release()
noexcept
;
Add the following as a new paragraph after paragraph 26:
void
promise::release()
noexcept
;
- Effects: Abandons any shared state (30.6.4).
4.4 future::get
Change 30.6.6 [futures.unique_future], class template future
, as indicated.
Change paragraph 15 as indicated:
Effects:
wait()
s until the shared state is ready, then retrieves the value stored in the shared state.releases the shared state (30.6.4).
5. References
-
[N4296] ISO/IEC JTC1 SC22 WG21, Programming Languages - C++, working draft, November 2014 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
[c++std-parallel-1162] If a shared state is made ready when there's noone around, does it make a sound?