66 lines
2.7 KiB
C++
66 lines
2.7 KiB
C++
/**
|
|
* @file chrono.h
|
|
* @author Nat Goodspeed
|
|
* @date 2021-10-05
|
|
* @brief supplement <chrono> with utility functions
|
|
*
|
|
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
|
|
* Copyright (c) 2021, Linden Research, Inc.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#if ! defined(LL_CHRONO_H)
|
|
#define LL_CHRONO_H
|
|
|
|
#include <chrono>
|
|
#include <type_traits> // std::enable_if
|
|
|
|
namespace LL
|
|
{
|
|
|
|
// time_point_cast() is derived from https://stackoverflow.com/a/35293183
|
|
// without the iteration: we think errors in the ~1 microsecond range are
|
|
// probably acceptable.
|
|
|
|
// This variant is for the optimal case when the source and dest use the same
|
|
// clock: that case is handled by std::chrono.
|
|
template <typename DestTimePoint, typename SrcTimePoint,
|
|
typename std::enable_if<std::is_same<typename DestTimePoint::clock,
|
|
typename SrcTimePoint::clock>::value,
|
|
bool>::type = true>
|
|
DestTimePoint time_point_cast(const SrcTimePoint& time)
|
|
{
|
|
return std::chrono::time_point_cast<typename DestTimePoint::duration>(time);
|
|
}
|
|
|
|
// This variant is for when the source and dest use different clocks -- see
|
|
// the linked StackOverflow answer, also Howard Hinnant's, for more context.
|
|
template <typename DestTimePoint, typename SrcTimePoint,
|
|
typename std::enable_if<! std::is_same<typename DestTimePoint::clock,
|
|
typename SrcTimePoint::clock>::value,
|
|
bool>::type = true>
|
|
DestTimePoint time_point_cast(const SrcTimePoint& time)
|
|
{
|
|
// The basic idea is that we must adjust the passed time_point by the
|
|
// difference between the clocks' epochs. But since time_point doesn't
|
|
// expose its epoch, we fall back on what each of them thinks is now().
|
|
// However, since we necessarily make sequential calls to those now()
|
|
// functions, the answers differ not only by the cycles spent executing
|
|
// those calls, but by potential OS interruptions between them. Try to
|
|
// reduce that error by capturing the source clock time both before and
|
|
// after the dest clock, and splitting the difference. Of course an
|
|
// interruption between two of these now() calls without a comparable
|
|
// interruption between the other two will skew the result, but better is
|
|
// more expensive.
|
|
const auto src_before = typename SrcTimePoint::clock::now();
|
|
const auto dest_now = typename DestTimePoint::clock::now();
|
|
const auto src_after = typename SrcTimePoint::clock::now();
|
|
const auto src_diff = src_after - src_before;
|
|
const auto src_now = src_before + src_diff / 2;
|
|
return dest_now + (time - src_now);
|
|
}
|
|
|
|
} // namespace LL
|
|
|
|
#endif /* ! defined(LL_CHRONO_H) */
|