[chrono] [thread] boost.chrono changes to boost::condition_variables

Hello, I've been exploring the boost.chrono candidate library as a basis for some fixes to boost::condition_variables::timed_wait(). I have some questions: 1) What is the current state of the chrono library, and its likely path into boost? 2) Are the chrono API changes to boost::condition_variables likely to be accepted into the boost.thread library in their current form? (i.e introduction of wait_for(), wait_until(), and the introduction of a new explicit dependency from boost/thread/condition_variables on chrono::time_point etc...) 3) Are there any other planned changes to boost::condition_variables::timed_wait to allow use of POSIX clocks other than CLOCK_REALTIME? My ultimate goal is to provide an implementation of boost::condition_variables that allows access to the POSIX clock CLOCK_MONOTONIC for use through boost::condition_variables::timed_wait(). Specifically, providing an implementation that uses pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) at boost::condition_variables initialisation, and use of pthread_cond_timedwait(..., &t) with clock_gettime(CLOCK_MONOTONIC, &t) in boost::condition_variables::timed_wait() and friends This change would provide reasonable behaviour of boost::condition_variables::time_wait() in presence of ongoing changes to the system clock. boost.chrono contributes to this goal by providing a clock abstraction and accessors to the appropriate POSIX clocks through class monotonic_clock, class system_clock, etc... Further to this, changes to boost::condition_variable::timed_wait() and similar would be required to use clock_gettime(CLOCK_MONOTONIC, &t) rather than boost::get_system_time() for calculations when the monotonic_clock is specified. I'm currently considering whether to trust that the boost.chrono API changes to the boost::condition_variables API are robust, and likely to persist for the future. Or to propose alternative changes localised to boost::condition_variables that achieve the goals above without the strong dependency on boost.chrono. Thanks for any comments or information about the above. Morgan http://www.bbc.co.uk/ This e-mail (and any attachments) is confidential and may contain personal views which are not the views of the BBC unless specifically stated. If you have received it in error, please delete it from your system. Do not use, copy or disclose the information in any way nor act in reliance on it and notify the sender immediately. Please note that the BBC monitors e-mails sent or received. Further communication will signify your consent to this.

On Jul 22, 2010, at 10:34 AM, Morgan Henry wrote:
Hello,
I've been exploring the boost.chrono candidate library as a basis for some fixes to boost::condition_variables::timed_wait().
I have some questions: 1) What is the current state of the chrono library, and its likely path into boost? 2) Are the chrono API changes to boost::condition_variables likely to be accepted into the boost.thread library in their current form? (i.e introduction of wait_for(), wait_until(), and the introduction of a new explicit dependency from boost/thread/condition_variables on chrono::time_point etc...) 3) Are there any other planned changes to boost::condition_variables::timed_wait to allow use of POSIX clocks other than CLOCK_REALTIME?
My ultimate goal is to provide an implementation of boost::condition_variables that allows access to the POSIX clock CLOCK_MONOTONIC for use through boost::condition_variables::timed_wait(). Specifically, providing an implementation that uses pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) at boost::condition_variables initialisation, and use of pthread_cond_timedwait(..., &t) with clock_gettime(CLOCK_MONOTONIC, &t) in boost::condition_variables::timed_wait() and friends This change would provide reasonable behaviour of boost::condition_variables::time_wait() in presence of ongoing changes to the system clock.
boost.chrono contributes to this goal by providing a clock abstraction and accessors to the appropriate POSIX clocks through class monotonic_clock, class system_clock, etc...
Further to this, changes to boost::condition_variable::timed_wait() and similar would be required to use clock_gettime(CLOCK_MONOTONIC, &t) rather than boost::get_system_time() for calculations when the monotonic_clock is specified.
I'm currently considering whether to trust that the boost.chrono API changes to the boost::condition_variables API are robust, and likely to persist for the future. Or to propose alternative changes localised to boost::condition_variables that achieve the goals above without the strong dependency on boost.chrono.
Thanks for any comments or information about the above. Morgan
http://www.bbc.co.uk/ This e-mail (and any attachments) is confidential and may contain personal views which are not the views of the BBC unless specifically stated. If you have received it in error, please delete it from your system. Do not use, copy or disclose the information in any way nor act in reliance on it and notify the sender immediately. Please note that the BBC monitors e-mails sent or received. Further communication will signify your consent to this.
Fwiw, boost::chrono is modeled after std::chrono set to be standardized in C++0X: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf Though this document is a final candidate draft, not a standard, I consider the <chrono> API fairly stable in this draft. -Howard

Howard Hinnant <howard.hinnant <at> gmail.com> writes:
Fwiw, boost::chrono is modeled after std::chrono set to be standardized in C++0X: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf
Thanks. That's useful guidance. I'll assume wait_for and wait_until overloads using std::chrono will become standard at some future point. That leads me to my next question - I believe the boost::condition_variable implementation has a bug when dealing with the monotonic clock. And the existing and proposed boost::condition_variable API prevent a bug free implementation.
My ultimate goal is to provide an implementation of boost::condition_variables that allows access to the POSIX clock CLOCK_MONOTONIC for use through boost::condition_variables::timed_wait(). Specifically, providing an implementation that uses pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) at boost::condition_variables initialisation, and use of pthread_cond_timedwait(..., &t) with clock_gettime(CLOCK_MONOTONIC, &t) in boost::condition_variables::timed_wait() and friends This change would provide reasonable behaviour of boost::condition_variables::time_wait() in presence of ongoing changes to the system clock.
Currently, the condition_variable implementation (existing and proposed) is strongly bound to the system clock for waits with time-outs; i.e. the "system clock" is used for timed waits irrespective of your desired time base. All wait_duration/rel_time overloads always assume CLOCK_REALTIME. template<typename duration_type> bool condition_variable::timed_wait(unique_lock<mutex>& m, duration_type const& wait_duration) { return timed_wait(m,boost::get_system_time()+wait_duration); } template <class Rep, class Period> bool condition_variable::wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time) { return (timed_wait(lock, convert_to<posix_time::time_duration>(rel_time)) } Even if you supply a chono::monotonic_clock::time_point (CLOCK_MONOTONIC) it is erroneously converted to a CLOCK_REALTIME. template <class Clock, class Duration> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time) { return timed_wait(lock, convert_to<system_time>(abs_time), pred); } bool condition_variable::timed_wait(unique_lock<mutex>& m, boost::system_time const& wait_until) The implementation _always_ assumes the POSIX CLOCK_REALTIME time base before forwarding its calculations to pthread_cond_timedwait. This creates bugs when you wish the condition_variable to operate in eg the CLOCK_MONOTONIC time base by using chono::monotonic_clock. In my view, the new boost::condition_variable API is incomplete, and lacks the ability to properly bind the condvar to one of the new clock types. Ideally, you would want time calculations to remain in the time base you selected. ie, it should use <clock_type>::now() for relative time calculations. (To do this, I've had to supply an additional clock_type template parameter.) template<typename clock_type, typename duration_type> bool condition_variable::wait_for(unique_lock<mutex>& m, const clock_type &clock, const duration_type &rel_time) { return wait_until(m, clock_type::now() + rel_time); } bool condition_variable::wait_until(unique_lock<mutex>& m, chrono::monotonic_clock::time_point const& wait_until) bool condition_variable::wait_until(unique_lock<mutex>& m, chrono::system_clock::time_point const& wait_until) Critically, this arrangement honours the required clock type, and allows the time parameter to be passed through to pthread_cond_timedwait(..., &t) unmolested. While the boost::condition_variable API remained ignorant of clocks other than "the system clock" this problem was just an inconvienience. However, now that condition variables have acquired the explicit capability to reference the POSIX CLOCK_MONOTONIC (through the monotonic_clock and time_point<monotonic_clock> classes), the API is _actively_ misleading. As for a solution, I think boost::condition_variables should become parameterised on the clock type - mixing 2 time bases in a single condition variable is broken. template<clock_type> class condition_variable_clocked; // backwards compatibility typedef boost::condition_variable<system_clock> condition_variable; // create a condvar that uses POSIX CLOCK_MONOTONIC boost::condition_variable<chrono::monotonic_clock> cv1; // create a condvar that uses POSIX CLOCK_REALTIME boost::condition_variable<chrono::system_clock> cv2; I'd appreciate any comments on the above. regards Morgan

I'll only address what the draft standard says, and what are potential changes. I leave it to others to decide what actually happens to the boost library... On Jul 23, 2010, at 10:18 AM, Morgan wrote:
Currently, the condition_variable implementation (existing and proposed) is strongly bound to the system clock for waits with time-outs; i.e. the "system clock" is used for timed waits irrespective of your desired time base.
All wait_duration/rel_time overloads always assume CLOCK_REALTIME.
template<typename duration_type> bool condition_variable::timed_wait(unique_lock<mutex>& m, duration_type const& wait_duration) { return timed_wait(m,boost::get_system_time()+wait_duration); }
template <class Rep, class Period> bool condition_variable::wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time) { return (timed_wait(lock, convert_to<posix_time::time_duration>(rel_time)) }
Even if you supply a chono::monotonic_clock::time_point (CLOCK_MONOTONIC) it is erroneously converted to a CLOCK_REALTIME.
template <class Clock, class Duration>
bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time) { return timed_wait(lock, convert_to<system_time>(abs_time), pred); }
bool condition_variable::timed_wait(unique_lock<mutex>& m, boost::system_time const& wait_until)
The implementation _always_ assumes the POSIX CLOCK_REALTIME time base before forwarding its calculations to pthread_cond_timedwait. This creates bugs when you wish the condition_variable to operate in eg the CLOCK_MONOTONIC time base by using chono::monotonic_clock.
[thread.req.timing]/2 says:
The member functions whose names end in _for take an argument that specifies a relative time. Implementations should use a monotonic clock to measure time for these functions. [ Note: Implementations are not required to use a monotonic clock because such a clock may not be available. — end note ]
In English: wait_for should always use a monotonic clock under the hood, unless the platform doesn't have one. More info: A future draft of the standard may change to say that platforms must have a monotonic clock. This has been proposed, but not yet debated. The debate will probably happen two weeks from now.
In my view, the new boost::condition_variable API is incomplete, and lacks the ability to properly bind the condvar to one of the new clock types. Ideally, you would want time calculations to remain in the time base you selected. ie, it should use <clock_type>::now() for relative time calculations. (To do this, I've had to supply an additional clock_type template parameter.)
template<typename clock_type, typename duration_type> bool condition_variable::wait_for(unique_lock<mutex>& m, const clock_type &clock, const duration_type &rel_time) { return wait_until(m, clock_type::now() + rel_time); }
bool condition_variable::wait_until(unique_lock<mutex>& m, chrono::monotonic_clock::time_point const& wait_until)
bool condition_variable::wait_until(unique_lock<mutex>& m, chrono::system_clock::time_point const& wait_until)
Critically, this arrangement honours the required clock type, and allows the time parameter to be passed through to pthread_cond_timedwait(..., &t) unmolested.
I do not believe templating wait_for on clock_type is a good idea as a monotonic clock (or the closest thing a platform has to it) should always be used for the *_for API.
While the boost::condition_variable API remained ignorant of clocks other than "the system clock" this problem was just an inconvienience. However, now that condition variables have acquired the explicit capability to reference the POSIX CLOCK_MONOTONIC (through the monotonic_clock and time_point<monotonic_clock> classes), the API is _actively_ misleading.
As for a solution, I think boost::condition_variables should become parameterised on the clock type - mixing 2 time bases in a single condition variable is broken.
template<clock_type> class condition_variable_clocked; // backwards compatibility typedef boost::condition_variable<system_clock> condition_variable;
// create a condvar that uses POSIX CLOCK_MONOTONIC boost::condition_variable<chrono::monotonic_clock> cv1;
// create a condvar that uses POSIX CLOCK_REALTIME boost::condition_variable<chrono::system_clock> cv2;
I'd appreciate any comments on the above.
Templating the condition_variable on clock_type was specifically avoided for std::condition_variable because a condition_variable often has more than one waiting client at a time, and it was felt that those different waiting clients should be able to use different clocks simultaneously when waiting on a (absolute) time_point. For example one client may wish to time out at 7pm tonight as measured by the system clock, while at the same time another client may wish to time out 200 milliseconds from the time it entered the start of the current function as measured by the monotonic clock. Such flexibility would not be possible if the clock type was encoded into the condition variable type. That being said, there is absolutely nothing wrong with creating a condition_variable_clocked<clock_type> that wraps condition_variable and imposes a specific clock, or family of clocks on the client. This might give needed control to the author of some type that needed to expose a cv-like object in its API. Such a cv-adaptor did not seem appropriate for standardization, but that doesn't mean it wouldn't be a good component of boost. Note that std::condition_variable_any ([thread.condition.condvarany]) is nothing more than a cv-adaptor. -Howard

Howard Hinnant <howard.hinnant <at> gmail.com> writes:
[thread.req.timing]/2 says:
The member functions whose names end in _for take an argument that specifies a relative time. Implementations should use a monotonic clock to measure time for these functions. [ Note: Implementations are not required to use a monotonic clock because such a clock may not be available. — end note ]
In English: wait_for should always use a monotonic clock under the hood, unless the platform doesn't have one.
Ah! Enabling and enforcing the use of the monotonic clock for relative timeouts feels instinctively correct; and just what I was after.
Templating the condition_variable on clock_type was specifically avoided for std::condition_variable because a condition_variable often has more than one waiting client at a time, and it was felt that those different waiting clients should be able to use different clocks simultaneously when waiting on a (absolute) time_point.
Of course; you are correct. I was being misled by the existing pthread implementation of boost::condition_variables and boost::chrono which currently enforces a single clock on all clients of a condvar. This limitation seems to have been inherited from pthread_cond_timedwait and pthread_cond_init which do not provide the fexibility for mixing of clock types for waiting clients of a cv. (The pthread_cond clock type is fixed at condvar creation). Fixing this limitation would now seem to be a problem for the pthread platform implementation, rather than the boost API and the standard. I'll propose some changes to boost::chrono pthread implementation in line with the draft standard. Many thanks for the very useful information. regards Morgan

On Jul 26, 2010, at 8:48 AM, Morgan wrote:
Templating the condition_variable on clock_type was specifically avoided for std::condition_variable because a condition_variable often has more than one waiting client at a time, and it was felt that those different waiting clients should be able to use different clocks simultaneously when waiting on a (absolute) time_point.
Of course; you are correct.
I was being misled by the existing pthread implementation of boost::condition_variables and boost::chrono which currently enforces a single clock on all clients of a condvar.
This limitation seems to have been inherited from pthread_cond_timedwait and pthread_cond_init which do not provide the fexibility for mixing of clock types for waiting clients of a cv. (The pthread_cond clock type is fixed at condvar creation).
Fixing this limitation would now seem to be a problem for the pthread platform implementation, rather than the boost API and the standard.
I'll propose some changes to boost::chrono pthread implementation in line with the draft standard.
There's more info on this topic here: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#887 -Howard
participants (3)
-
Howard Hinnant
-
Morgan
-
Morgan Henry