891. std::thread, std::call_once issue

Section: 32.4.3.3 [thread.thread.constr], 32.6.7.2 [thread.once.callonce] Status: C++11 Submitter: Peter Dimov Opened: 2008-09-15 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [thread.thread.constr].

View all issues with C++11 status.

Discussion:

I notice that the vararg overloads of std::thread and std::call_once (N2723 32.4.3.3 [thread.thread.constr] and 32.6.7.2 [thread.once.callonce]) are no longer specified in terms of std::bind; instead, some of the std::bind wording has been inlined into the specification.

There are two problems with this.

First, the specification (and implementation) in terms of std::bind allows, for example:

std::thread th( f, 1, std::bind( g ) );

which executes f( 1, g() ) in a thread. This can be useful. The "inlined" formulation changes it to execute f( 1, bind(g) ) in a thread.

Second, assuming that we don't want the above, the specification has copied the wording

INVOKE(func, w1, w2, ..., wN) (20.6.2) shall be a valid expression for some values w1, w2, ..., wN

but this is not needed since we know that our argument list is args; it should simply be

INVOKE(func, args...) (20.6.2) shall be a valid expression

[ Summit: ]

Move to open.

[ Post Summit Anthony provided proposed wording. ]

[ 2009-07 Frankfurt ]

Leave Open. Await decision for thread variadic constructor.

[ 2009-10 Santa Cruz: ]

See proposed wording for 929 for this, for the formulation on how to solve this. 929 modifies the thread constructor to have "pass by value" behavior with pass by reference efficiency through the use of the decay trait. This same formula would be useful for call_once.

[ 2010-02-11 Anthony updates wording. ]

[ 2010-02-12 Moved to Tentatively Ready after 5 postive votes on c++std-lib. ]

Proposed resolution:

Modify 32.6.7.2 [thread.once.callonce] p1-p2 with the following:

template<class Callable, class ...Args>
  void call_once(once_flag& flag, Callable&& func, Args&&... args);

Given a function as follows:


template<typename T> typename decay<T>::type decay_copy(T&& v)
   { return std::forward<T>(v); }

1 Requires: The template parameters Callable and each Ti in Args shall be CopyConstructible if an lvalue and otherwise satisfy the MoveConstructible requirements. INVOKE(decay_copy(std::forward<Callable>(func), w1, w2, ..., wN decay_copy(std::forward<Args>(args))...) (22.10.4 [func.require]) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(Args).

2 Effects: Calls to call_once on the same once_flag object are serialized. If there has been a prior effective call to call_once on the same once_flag object, the call to call_once returns without invoking func. If there has been no prior effective call to call_once on the same once_flag object, the argument func (or a copy thereof) is called as if by invoking func(args) INVOKE(decay_copy(std::forward<Callable>(func)), decay_copy(std::forward<Args>(args))...) is executed. The call to call_once is effective if and only if func(args) INVOKE(decay_copy(std::forward<Callable>(func)), decay_copy(std::forward<Args>(args))...) returns without throwing an exception. If an exception is thrown it is propagated to the caller.