Nov 30, 2021

bsl::chrono Integration with bsl and bdl

Overview

In BDE 3.86 support for bsl::chrono::duration and bsl::chrono::time_point was added to bslmt and bdlmt. This enables users who prefer using bsl::chrono::duration and bsl::chrono::time_point to better integrate with the BDE libraries. Specifically, conversions between bsl::chrono::duration and bsls::TimeInterval were added, and support for bsl::chrono::time_point was added to the synchronization primitives in bslmt, as well as the classes bslmt::ThreadUtil, bdlmt::EventScheduler, and bdlmt::Throttle.

bsl::chrono::duration and bsls::TimeInterval

The BDE types bsls::TimeInterval and bsl::chrono::duration are both representations of a time interval, but bsls::TimeInterval is a concrete type (representing an interval in seconds and nanoseconds), while bsl::chrono::duration is a template whose representation, precision, and range are determined by its template parameters. BDE now provides a set of operations to convert between bsls::TimeInterval and bsl::chrono::duration. These include:

Restrictions:

For example, to create a bsls::TimeInterval interval, add a second, and then convert the bsls::TimeInterval interval to a duration holding an integer of seconds:

bsls::TimeInterval interval;
interval.addDuration(bsl::chrono::seconds(1));
bsl::chrono::seconds duration = interval.asDuration<bsl::chrono::seconds>();

Potentially Lossy Conversions

The conversion from bsl::chrono::duration to bsls::TimeInterval is implicit if the conversion can be done without loss of precision, whereas it must be explicit if the conversion is potentially lossy. For example, if we define a duration in picoseconds, this requires a potentially lossy conversion when converted to a bsls::TimeInterval (defined in terms of nanoseconds). Hence, the following code does not compile (the attempted conversion would require use of the implicit bsls::TimeInterval user-defined conversion):

typedef bsl::chrono::duration<unsigned long long, bsl::ratio<1, 1000000000000> > Picoseconds;
Picoseconds d(5001);
bsls::TimeInterval ti = d;  // does not compile: implicit conversion unavailable

The following example will compile because the conversion to bsls::TimeInterval is explicit:

Picoseconds d(5001);
bsls::TimeInterval ti = bsls::TimeInterval(d);  // does compile: initial explicit conversion

In contrast, a duration defined in milliseconds can be converted to a bsls::TimeInterval without a loss of precision, so the following example will compile:

bsl::chrono::milliseconds d(5001);  // does compile (non-lossy conversion)
bsls::TimeInterval ti = d;

Note that just because a bsl::chrono::duration type can be converted to a bsls::TimeInterval without loss of precision (meaning an implicit conversion is available) does not guarantee that the value for that bsl::chrono::duration is in the range that can be represented by bsls::TimeInterval.

Note

Converting a bsl::chrono::duration value that is outside of the representable range of bsls::TimeInterval results in undefined behavior (see bsls::TimeInterval::isValid).

Using bsls::TimeInterval with Literals

A set of user-defined literals (UDL) has been provided to form bsl::chrono::duration objects with various duration periods such as hours, minutes, seconds, milliseconds, microseconds and nanoseconds. The ud-suffixes are preceded with the ‘_’ symbol to distinguish between the bsl::chrono UDLs and the std::chrono UDLs introduced in the C++14 standard and implemented in the standard library.

using namespace bsl::chrono_literals;
bsls::TimeInterval elapsedTime = 5_s;

Using bsls::TimeInterval with Helper Types

The standard helper types for time units are available in the bsl namespace.

bsls::TimeInterval elapsedTime(bsl::chrono::microseconds(500));
bsl::chrono::microseconds duration = elapsedTime.asDuration<bsl::chrono::microseconds>();

bsls::SystemClockType versus bsl::chrono Clocks

On all production (and tested) platforms, bsls::SystemClockType::e_REALTIME is equivalent to std::chrono::system_clock and bsls::SystemClockType::e_MONOTONIC is equivalent to std::chrono::steady_clock (meaning the BDE clock-type and the matching standard library clock can be used interchangeably). If a platform is found where this is not the case, bsl::chrono::system_clock and bsl::chrono::steady_clock will be modified to match the ones indicated by bsls::SystemClockType. As such, we strongly recommend using bsl::chrono when working with BDE types.

bslmt::ChronoUtil

This struct provides a namespace for utility functions that operate on bsl::chrono facilities. Presently, it contains four methods:

The method bslmt::ChronoUtil::durationToTimeInterval returns a bsls::TimeInterval for the provided duration, and allows durations with floating-point representations. The method bslmt::ChronoUtil::isMatchingClock returns true if the specified (template parameter) clock matches the clock indicated by a bsls::SystemClockType value. The two bslmt::ChronoUtil::timedWait methods are helpers for implementing higher-level timedWait operations (e.g., bslmt::TimedSemaphore::timedWait) using the logic explained in Enhancements to bslmt Components to Support bsl::chrono.

Converting a Floating Point bsl::chrono::duration

The method bslmt::ChronoUtil::durationToTimeInterval make a best effort conversion to a bsls::TimeInterval from any bsl::chrono::duration.

bsls::TimeInterval fp = bslmt::ChronoUtil::durationToTimeInterval(
                                         bsl::chrono::duration<double>(6.5));

Enhancements to bslmt Components to Support bsl::chrono

The classes in bslmt that accept a bsls::SystemClockType in their constructor were augmented to accept bsl::chrono::system_clock and bsl::chrono::steady_clock (user defined clocks are not accepted in the constructor). Additionally, the timeWait methods were augmented to accept a bsl::chrono::time_point, with an arbitrary clock.

Note

It is strongly recommended to supply bsl::chrono::time_point objects that use the same clock as the one used to construct the bslmt object.

If the clock of the bsl::chrono::time_point does not match the clock provided to the constructor, the timed-wait method must make an addition comparison against time_point::clock::now() (and potentially, do this repeatedly) to ensure the contract of the method is guaranteed (the function does not time out before the specified bsl::chrono::time_point has occurred). If the clock of the bsl::chrono::time_point matches the clock provided to the constructor, the timed-wait method does not need an additional comparison against time_point::clock::now() (potentially multiple comparisons) and hence is more efficient.

The following classes were augmented:

For example, to use a bsl::chrono::time_point with bslmt::Condition:

using namespace bsl::chrono_literals;

// Using the same clock as the time_points supplied to timedWait is
// highly recommended.
bslmt::Condition condition((bsl::chrono::steady_clock()));
bslmt::Mutex     mutex;

int rc = condition.timedWait(&mutex, bsl::chrono::steady_clock::now() + 5_s);
if (0 == rc) {
    // condition signalled, do something
}
else if (bslmt::Condition::e_TIMED_OUT == rc) {
    // timeout
}
else {
    // error
}

Miscelaneous Interface Enhancements

bdlmt::EventScheduler

The class bdlmt::EventScheduler was augmented similarly to bslmt, but instead of timed-wait methods being added, all the scheduling and rescheduling methods were augmented to accept a bsl::chrono::time_point. Overloads accepting a time_point were added to the following methods:

bdlmt::Throttle

The class bdlmt::Throttle had its initialization methods augmented to accept bsl::chrono::system_clock and bsl::chrono::steady_clock:

No other changes were made (please see this component’s documentation for an explanation).