// bslmt_chronoutil.h                                                 -*-C++-*-
#ifndef INCLUDED_BSLMT_CHRONOUTIL
#define INCLUDED_BSLMT_CHRONOUTIL

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide utilities related to threading with C++11-style clocks.
//
//@CLASSES:
//  bslmt::ChronoUtil: namespace for 'bsl::chrono'-related operations
//
//@SEE_ALSO:
//
//@DESCRIPTION: This component defines a utility 'struct', 'bslmt::ChronoUtil',
// that serves as a namespace for a suite of classes and functions for
// interfacing C++11-style clocks with the clocks that BDE provides.
//
// 'bslmt::ChronoUtil' defines a 'durationToTimeInterval' function for
// converting an arbitrary 'bsl::chrono::duration' to a 'bsls::TimeInterval'.
//
// 'bslmt::ChronoUtil' also defines a 'timedWait' function for waiting on a
// synchronization primitive that uses a 'bsls' system clock type internally
// (see {'bsls_systemclocktype'}), while allowing the user to specify the
// timeout using a 'bsl::chrono::time_point'.
//
// 'ChronoUtil::timedWait' requires several things from the underlying
// primitive:
//
// (1) an enumeration containing 'e_TIMED_OUT'.
//
// (2) a member function, 'timedWait', that takes a 'const bsls::TimeInterval&'
// denoting the timeout value, and possibly an additional pointer argument.
// This function should return 0 upon success, 'e_TIMED_OUT' on a timeout, and
// some other value on failure.  'ChronoUtil::timedWait' will convert the
// 'bsl::chrono::time_point' that is passed to it into a 'bsls::TimeInterval'
// that can be used by the synchronization primitive ('ARG_TYPE' is just a
// placeholder name; the primitive will have its own type):
//..
//  int timedWait(const bsls::TimeInterval&);
//  int timedWait(ARG_TYPE *argument, const bsls::TimeInterval&);
//..
//
// (3) a 'const' member function, 'clockType', that takes no parameters and
// returns the underlying clock that the primitive uses -- either
// 'bsls::SystemClockType::e_REALTIME' or 'bsls::SystemClockType::e_MONOTONIC':
//..
//  bsls::SystemClockType::Enum clockType() const;
//..
//
// Note that if the clock associated with the time point does not correspond to
// the clock used by the underlying synchronization primitive, then the
// 'timedWait' function of the primitive may be called more than once, so the
// method is potentially more efficient if the clocks match.
//
// Finally, 'bslmt::ChronoUtil' defines an 'isMatchingClock' function that
// checks to see if a C++11-style clock matches a 'bsls' system clock.  See
// {'bsls_systemclocktype'}.
//
///Usage
///-----
// This example illustrates intended use of this component.
//
// We first define a synchronization primitive that is compliant with the
// requirements of 'bslmt::ChronoUtil::timedWait' and then demonstrate use of
// that primitive with 'timedWait'.
//
///Prologue: Create a Synchronization Primitive
/// - - - - - - - - - - - - - - - - - - - - - -
// The 'TimedWaitSuccess' class, defined below, is a synchronization primitive
// that complies with the requirements of 'bslmt::ChronoUtil::timedWait' (see
// the component-level documentation for information).
//
// First, we define the interface of 'TimedWaitSuccess':
//..
//  class TimedWaitSuccess {
//      // 'TimedWaitSuccess' is a synchronization primitive that always
//      // succeeds.
//
//    private:
//      // DATA
//      bsls::SystemClockType::Enum d_clockType;
//
//    public:
//      // TYPES
//      enum { e_TIMED_OUT = 1 };
//
//      // CREATORS
//      explicit
//      TimedWaitSuccess(bsls::SystemClockType::Enum clockType
//                                        = bsls::SystemClockType::e_REALTIME);
//          // Create a 'TimedWaitSuccess' object.  Optionally specify a
//          // 'clockType' indicating the type of the system clock against
//          // which the 'bsls::TimeInterval' 'absTime' timeouts passed to the
//          // 'timedWait' method are to be interpreted.  If 'clockType' is not
//          // specified then the realtime system clock is used.
//
//      // MANIPULATORS
//      int timedWait(const bsls::TimeInterval&);
//          // Return 0 immediately.  Note that this is for demonstration and
//          // testing purposes only.
//
//      // ACCESSORS
//      bsls::SystemClockType::Enum clockType() const;
//          // Return the clock type used for timeouts.
//  };
//..
// Then, we implement the creator.  All it has to do is remember the
// 'clockType' that was passed to it:
//..
//  inline
//  TimedWaitSuccess::TimedWaitSuccess(bsls::SystemClockType::Enum clockType)
//  : d_clockType(clockType)
//  {
//  }
//
//..
// Next, we implement the 'timedWait' function.  In this simplistic primitive,
// this function always succeeds:
//..
//  // MANIPULATORS
//  inline
//  int TimedWaitSuccess::timedWait(const bsls::TimeInterval&)
//  {
//      return 0;
//  }
//
//..
// Next, we implement the 'clockType' function, which returns the underlying
// 'bsls::SystemClockType::Enum' that this primitive uses:
//..
//  // ACCESSORS
//  inline
//  bsls::SystemClockType::Enum TimedWaitSuccess::clockType() const
//  {
//      return d_clockType;
//  }
//..
//
///Example 1: Using 'bslmt::ChronoUtil::timedWait'
///- - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates use of 'bslmt::ChronoUtil::timedWait' to block on
// a synchronization primitive until either a condition is satisfied or a
// specified amount of time has elapsed.  We use a 'bsl::chrono::time_point' to
// specify the amount of time to wait.  To do this, we call
// 'bslmt::ChronoUtil::timedWait', passing in the timeout as an *absolute* time
// point.  In this example, we're using 'TimedWaitSuccess' as the
// synchronization primitive, and specifying the timeout using
// 'bsl::chrono::steady_clock'.
//
// First, we construct the 'TimedWaitSuccess' primitive; by default it uses the
// 'bsls' realtime system clock to measure time:
//..
//  TimedWaitSuccess aPrimitive;
//..
// Then, we call 'bslmt::ChronoUtil::timedWait' to block on 'aPrimitive', while
// passing a timeout of "10 seconds from now", measured on the
// 'bsl::chrono::steady_clock':
//..
//  int rc = bslmt::ChronoUtil::timedWait(
//           &aPrimitive,
//           bsl::chrono::steady_clock::now() + bsl::chrono::seconds(10));
//..
// When this call returns, one of three things will be true: (a) 'rc == 0',
// which means that the call succeeded before the timeout expired, (b)
// 'rc == TimedWaitSuccess::e_TIMED_OUT', which means that the call did not
// succeed before the timeout expired, or (c) 'rc' equals some other value,
// which means that an error occurred.
//
// If the call to 'bslmt::ChronoUtil::timedWait' returned 'e_TIMED_OUT' then we
// are guaranteed that the current time *on the clock that the time point was
// defined on* is greater than the timeout value that was passed in.

#include <bslscm_version.h>

#include <bsls_libraryfeatures.h>
#include <bsls_systemclocktype.h>
#include <bsls_systemtime.h>
#include <bsls_timeinterval.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#include <bsl_chrono.h>

namespace BloombergLP {
namespace bslmt {

                           // =================
                           // struct ChronoUtil
                           // =================

struct ChronoUtil {
    // This 'struct' provides a namespace for utility functions that operate on
    // 'bsl::chrono' facilities.

  public:
    // CLASS METHODS
    template <class REP_TYPE, class PERIOD_TYPE>
    static bsls::TimeInterval durationToTimeInterval(
                 const bsl::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration);
        // Return a 'bsls::TimeInterval' having the value represented by the
        // specified 'duration'.  Unlike the implicit conversion defined from
        // duration defined in 'bsls::TimeInterval', this conversion handles
        // floating-point-based durations as well as integral ones.

    template <class CLOCK>
    static
    bool isMatchingClock(bsls::SystemClockType::Enum clockType);
        // Return 'true' if the specified (template parameter) type 'CLOCK'
        // matches the specified 'clockType', and 'false' otherwise.

    template <class PRIMITIVE, class CLOCK, class DURATION>
    static
    int timedWait(PRIMITIVE                                       *primitive,
                  const bsl::chrono::time_point<CLOCK, DURATION>&  absTime);
        // Block on the specified 'primitive' object of the (template
        // parameter) 'PRIMITIVE' type by calling its 'timedWait' method,
        // passing a timeout calculated from the specified 'absTime'.
        // 'absTime' is an *absolute* time represented by a time point with
        // respect to some epoch, which is determined by the clock associated
        // with the time point.  Return 0 on success, 'PRIMITIVE::e_TIMED_OUT'
        // if the 'absTime' timeout expired, and other return values on error.
        // The 'timedWait' method of 'primitive' is called only once if the
        // clock type specified by the (template parameter) type 'CLOCK'
        // corresponds to the clock used by 'primitive', and may be called more
        // than once otherwise.  Note that error codes returned from this
        // method, necessarily distinct from 0 and 'PRIMITIVE::e_TIMED_OUT',
        // are defined by 'PRIMITIVE'.

    template <class PRIMITIVE, class ARG_TYPE, class CLOCK, class DURATION>
    static
    int timedWait(PRIMITIVE                                       *primitive,
                  ARG_TYPE                                        *argument,
                  const bsl::chrono::time_point<CLOCK, DURATION>&  absTime);
        // Block on the specified 'primitive' object of the (template
        // parameter) 'PRIMITIVE' type by calling its 'timedWait' method,
        // passing the specified 'argument' of the (template parameter)
        // 'ARG_TYPE' and a timeout calculated from the specified 'absTime'.
        // 'absTime' is an *absolute* time represented by a time point with
        // respect to some epoch, which is determined by the clock associated
        // with the time point.  Return 0 on success, 'PRIMITIVE::e_TIMED_OUT'
        // if the 'absTime' timeout expired, and other return values on error.
        // The 'timedWait' method of 'primitive' is called only once if the
        // clock type specified by the (template parameter) type 'CLOCK'
        // corresponds to the clock used by 'primitive', and may be called more
        // than once otherwise.  Note that error codes returned from this
        // method, necessarily distinct from 0 and 'PRIMITIVE::e_TIMED_OUT',
        // are defined by 'PRIMITIVE'.
};

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                           // -----------------
                           // struct ChronoUtil
                           // -----------------

// CLASS METHODS
template <class REP_TYPE, class PERIOD_TYPE>
inline
bsls::TimeInterval ChronoUtil::durationToTimeInterval(
                  const bsl::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration)
{
    using namespace bsl::chrono;

    // Explicit scope needed due to legacy code using 'seconds' at BloombergLP
    // namespace scope.
    bsl::chrono::seconds wholeSecs =
        duration_cast<bsl::chrono::seconds>(duration);
    nanoseconds nanoSecs  = duration_cast<nanoseconds>(duration - wholeSecs);
    return bsls::TimeInterval(wholeSecs.count(),
                              static_cast<int>(nanoSecs.count()));
}

template <class CLOCK>
inline
bool ChronoUtil::isMatchingClock(bsls::SystemClockType::Enum)
{
    return false;
}

template <>
inline
bool ChronoUtil::isMatchingClock<bsl::chrono::system_clock>(
                                         bsls::SystemClockType::Enum clockType)
{
    return bsls::SystemClockType::e_REALTIME == clockType;
}

template <>
inline
bool ChronoUtil::isMatchingClock<bsl::chrono::steady_clock>(
                                         bsls::SystemClockType::Enum clockType)
{
    return bsls::SystemClockType::e_MONOTONIC == clockType;
}

template <class PRIMITIVE, class CLOCK, class DURATION>
int ChronoUtil::timedWait(
                    PRIMITIVE                                       *primitive,
                    const bsl::chrono::time_point<CLOCK, DURATION>&  absTime)
{
    if (ChronoUtil::isMatchingClock<CLOCK>(primitive->clockType())) {
        return primitive->timedWait(
                     bsls::TimeInterval(absTime.time_since_epoch())); // RETURN
    }
    else {
        typename CLOCK::time_point now = CLOCK::now();
        int                        ret;

        // Iteration is necessary because the specified 'CLOCK' type is known
        // to be different from that used internally by 'primitive'.

        while (absTime > now) {
            bsls::TimeInterval ti =
                                  bsls::SystemTime::now(primitive->clockType())
                                                   .addDuration(absTime - now);
            ret = primitive->timedWait(ti);
            if (PRIMITIVE::e_TIMED_OUT != ret) {
                return ret;                                           // RETURN
            }
            now = CLOCK::now();
        }
        return PRIMITIVE::e_TIMED_OUT;                                // RETURN
    }
}

template <class PRIMITIVE, class ARG_TYPE, class CLOCK, class DURATION>
int ChronoUtil::timedWait(
                    PRIMITIVE                                       *primitive,
                    ARG_TYPE                                        *argument,
                    const bsl::chrono::time_point<CLOCK, DURATION>&  absTime)
{
    if (ChronoUtil::isMatchingClock<CLOCK>(primitive->clockType())) {
        return primitive->timedWait(
                     argument,
                     bsls::TimeInterval(absTime.time_since_epoch())); // RETURN
    }
    else {
        typename CLOCK::time_point now = CLOCK::now();
        int                        ret;

        // Iteration is necessary because the specified 'CLOCK' type is known
        // to be different from that used internally by 'primitive'.

        while (absTime > now) {
            bsls::TimeInterval ti =
                                  bsls::SystemTime::now(primitive->clockType())
                                                   .addDuration(absTime - now);
            ret = primitive->timedWait(argument, ti);
            if (PRIMITIVE::e_TIMED_OUT != ret) {
                return ret;                                           // RETURN
            }
            now = CLOCK::now();
        }
        return PRIMITIVE::e_TIMED_OUT;                                // RETURN
    }
}

}  // close package namespace
}  // close enterprise namespace

#endif // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#endif

// ----------------------------------------------------------------------------
// Copyright 2021 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------