// bsls_stopwatch.h                                                   -*-C++-*-
#ifndef INCLUDED_BSLS_STOPWATCH
#define INCLUDED_BSLS_STOPWATCH

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

//@PURPOSE: Provide access to user, system, and wall times of current process.
//
//@CLASSES:
//  bsls::Stopwatch: accumulates user, system, wall times of current process
//
//@DESCRIPTION: This component provides a class, `bsls::Stopwatch`, that
// implements real-time (system clock) interval timers for the system, user,
// and wall times of the current process.  A `bsls::Stopwatch` object can
// accumulate the above values from multiple runs and always presents only the
// final total (zero if never started or reset to the initial state).
//
///Accuracy and Precision
///----------------------
// A `bsls::Stopwatch` object returns its elapsed time intervals in seconds as
// `double` values.  The precision is given by that of the `bsls::TimeUtil`
// component, and, as such, strives to be as high as possible.  Monotonic
// behavior is platform-dependent, however, as are accuracy and useful
// precision.  The user is advised to determine the actual performance on each
// platform of interest.  In general, it is better to avoid stopping and
// restarting the stopwatch too often (e.g., inside a loop).  It is better to
// measure the overhead of the loop separately and subtract that time from the
// over-all time interval.
//
///Accuracy on Windows
///- - - - - - - - - -
// `bsls::Stopwatch` may be slow or inconsistent on some Windows machines.  See
// the `Accuracy and Precision` section of `bsls_timeutil.h`.
//
///Usage Examples
///--------------
// The following snippets of code illustrate basic use of a `bsls::Stopwatch`
// object.  First we create a stopwatch and note that the accumulated times are
// all initially 0.0:
// ```
// bsls::Stopwatch s;
// const double t0s = s.accumulatedSystemTime();  assert(0.0 == t0s);
// const double t0u = s.accumulatedUserTime();    assert(0.0 == t0u);
// const double t0w = s.accumulatedWallTime();    assert(0.0 == t0w);
// ```
// Next we start the stopwatch such that it does not accumulate system or user
// times.  Note that a stopwatch always accumulates wall time (i.e., as long as
// it is in the RUNNING state):
// ```
// s.start();
// const double t1s = s.accumulatedSystemTime();  assert(0.0 == t1s);
// const double t1u = s.accumulatedUserTime();    assert(0.0 == t1u);
// const double t1w = s.accumulatedWallTime();    assert(0.0 <= t1w);
// ```
// Now stop the stopwatch and restart it so as to accumulate system and user
// times (i.e., by passing `true` to the `start` method):
// ```
// s.stop();
// const double t2s = s.accumulatedSystemTime();  assert(t1s == t2s);
// const double t2u = s.accumulatedUserTime();    assert(t1u == t2u);
// const double t2w = s.accumulatedWallTime();    assert(t1w <= t2w);
//
// s.start(bsls::Stopwatch::k_COLLECT_ALSO_CPU_TIMES);
// const double t3s = s.accumulatedSystemTime();  assert(t2s <= t3s);
// const double t3u = s.accumulatedUserTime();    assert(t2u <= t3u);
// const double t3w = s.accumulatedWallTime();    assert(t2w <= t3w);
// ```
// Finally, we reset the stopwatch, which both puts it into the STOPPED state
// and resets all accumulated times back to their initial state (i.e., 0.0):
// ```
// s.reset();
// const double t4s = s.accumulatedSystemTime();  assert(0.0 == t4s);
// const double t4u = s.accumulatedUserTime();    assert(0.0 == t4u);
// const double t4w = s.accumulatedWallTime();    assert(0.0 == t4w);
// const double t5s = s.accumulatedSystemTime();  assert(0.0 == t5s);
// const double t5u = s.accumulatedUserTime();    assert(0.0 == t5u);
// const double t5w = s.accumulatedWallTime();    assert(0.0 == t5w);
// ```

#include <bsls_keyword.h>
#include <bsls_timeutil.h>
#include <bsls_types.h>

#include <string.h>

namespace BloombergLP {
namespace bsls {

                             // ===============
                             // class Stopwatch
                             // ===============

/// The `class` provides an accumulator for the system, user, and wall times
/// of the current process.  A stopwatch can be in either the STOPPED
/// (initial) state or the RUNNING state.  It potentially tracks three
/// values: the accumulated system time, the accumulated user time, and the
/// accumulated wall time (all in seconds and all initially set to zero).
/// Whether or not system and user times are accumulated is conditional on
/// how the stopwatch is started (see the `start` method).  While in the
/// RUNNING state, a stopwatch accumulates the above values and it retains
/// the values if put into the STOPPED state (unless `reset` is called).
/// The accumulated times can be accessed at any time and in either state
/// (RUNNING or STOPPED).
class Stopwatch {

    // DATA
    Types::Int64 d_startSystemTime;        // system time when started
                                           // (nanoseconds)

    Types::Int64 d_startUserTime;          // user time when started
                                           // (nanoseconds)

    TimeUtil::OpaqueNativeTime d_startWallTime;
                                           // wall time when started
                                           // (nanoseconds)

    Types::Int64 d_accumulatedSystemTime;  // accumulated system time
                                           // (nanoseconds)

    Types::Int64 d_accumulatedUserTime;    // accumulated user time
                                           // (nanoseconds)

    Types::Int64 d_accumulatedWallTime;    // accumulated wall time
                                           // (nanoseconds)

    bool         d_isRunning;              // state flag ('true' if RUNNING,
                                           // 'false' if STOPPED)

    bool         d_collectCpuTimesFlag;    // 'true' if cpu times are being
                                           // collected

    // CLASS DATA
    static const double      s_nanosecondsPerSecond;   // conversion factor
                                                       // (for nanoseconds to
                                                       // seconds)

  private:
    // NOT IMPLEMENTED
    Stopwatch& operator=(const Stopwatch&) BSLS_KEYWORD_DELETED;

  private:
    // PRIVATE MANIPULATORS

    /// Update the CPU times accumulated but this stopwatch.
    void updateTimes();

    // PRIVATE ACCESSORS

    /// Load into the specified `systemTime`, `userTime`, and `wallTime` the
    /// values of the system time, user time, and wall time (in
    /// nanoseconds), respectively, as provided by `TimeUtil`.
    void accumulatedTimesRaw(Types::Int64               *systemTime,
                             Types::Int64               *userTime,
                             TimeUtil::OpaqueNativeTime *wallTime) const;

    /// Return the elapsed time, in nanoseconds, between the current
    /// `d_startWallTime` and the specified `rawWallTime`.
    Types::Int64 elapsedWallTime(TimeUtil::OpaqueNativeTime rawWallTime) const;

  public:
    // PUBLIC CONSTANTS
    static const bool k_COLLECT_WALL_TIME_ONLY     = false;

    /// For readability/ease of understanding of code that calls the
    /// `start(bool)` method use these constants as arguments.
    static const bool k_COLLECT_WALL_AND_CPU_TIMES = true;

    // CREATORS

    /// Create a stopwatch in the STOPPED state having total accumulated
    /// system, user, and wall times all equal to 0.0.
    Stopwatch();

#ifdef BSLS_COMPILERFEATURES_SUPPORT_DEFAULTED_FUNCTIONS
    // To avoid warnings about future incompatibility due to the deleted copy
    // assignment operator we declare the copy constructor as implicitly
    // generated.  For consistency the destructor was also placed here and
    // declared to be explicitly generated.

    /// Create a stopwatch having the state and total accumulated system,
    /// user, and wall time of the specified `other` object.  Note that this
    /// method's definition is compiler generated.
    Stopwatch(const Stopwatch& other) = default;

    /// Destroy this stopwatch.  Note that this method's definition is
    /// compiler generated.
    ~Stopwatch() = default;
#endif

    // MANIPULATORS

    /// Place this stopwatch in the STOPPED state, unconditionally stopping
    /// the accumulation of elapsed times, and set the quiescent elapsed
    /// times to 0.0.
    void reset();

    /// Place this stopwatch in the RUNNING state and begin accumulating
    /// elapsed times if this object was in the STOPPED state.  Optionally
    /// specify a `collectCpuTimes` flag indicating whether CPU times should
    /// be collected.  If `collectCpuTimes` is not specified, then CPU times
    /// are *not* collected.  Note that the instantaneous total elapsed
    /// times are available from the RUNNING state.  Also note that
    /// disabling collection of CPU times will result in fewer systems calls
    /// and therefore faster measurements.  Also note that `collectCpuTimes`
    /// may be expressed using the one of the class level constants
    /// `k_COLLECT_WALL_TIME_ONLY`, and `k_COLLECT_WALL_AND_CPU_TIMES` for
    /// easier immediate understanding of the meaning of the client code.
    void start(bool collectCpuTimes = false);

    /// Place this stopwatch in the STOPPED state, unconditionally stopping
    /// the accumulation of elapsed times.  Note that the quiescent
    /// accumulated elapsed times are available while in the STOPPED state.
    void stop();

    // ACCESSORS

    /// Return the total (instantaneous and quiescent) elapsed system time
    /// (in seconds) accumulated by this stopwatch, or 0 if the collection
    /// of CPU times is disabled.
    double accumulatedSystemTime() const;

    /// Load into the specified `systemTime`, `userTime` and `wallTime` the
    /// total (instantaneous and quiescent) elapsed system, user, and wall
    /// times (all in seconds) accumulated by this stopwatch.  Note that
    /// this method attempts to retrieve all of the values at the same time
    /// (atomically), if the underlying platform supports it.
    void accumulatedTimes(double *systemTime,
                          double *userTime,
                          double *wallTime) const;

    /// Return the total (instantaneous and quiescent) elapsed user time (in
    /// seconds) accumulated by this stopwatch, or 0 if the collection of
    /// CPU times is disabled.
    double accumulatedUserTime() const;

    /// Return the total (instantaneous and quiescent) elapsed wall time (in
    /// seconds) accumulated by this stopwatch.
    double accumulatedWallTime() const;

    /// Return the total (instantaneous and quiescent) elapsed wall time (in
    /// seconds) accumulated by this stopwatch.  Note that this method is
    /// equivalent to `accumulatedWallTime`.
    double elapsedTime() const;

    /// Return `true` if this stopwatch is in the RUNNING state, and `false`
    /// otherwise.
    bool isRunning() const;
};

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

                             // ---------------
                             // class Stopwatch
                             // ---------------

// PRIVATE ACCESSORS
inline
void Stopwatch::accumulatedTimesRaw(Types::Int64               *systemTime,
                                    Types::Int64               *userTime,
                                    TimeUtil::OpaqueNativeTime *wallTime) const
{
    TimeUtil::getProcessTimers(systemTime, userTime);
    TimeUtil::getTimerRaw(wallTime);
}

inline
Types::Int64 Stopwatch::elapsedWallTime(
                                  TimeUtil::OpaqueNativeTime rawWallTime) const
{
    return TimeUtil::convertRawTime(rawWallTime)
         - TimeUtil::convertRawTime(d_startWallTime);
}

// CREATORS
inline
Stopwatch::Stopwatch()
: d_startSystemTime(0)
, d_startUserTime(0)
// , d_startWallTime(0)  // opaque type, no default ctor from 0.
, d_accumulatedSystemTime(0)
, d_accumulatedUserTime(0)
, d_accumulatedWallTime(0)
, d_isRunning(false)
, d_collectCpuTimesFlag(false)
{
    TimeUtil::initialize();
    memset(&d_startWallTime, 0, sizeof(d_startWallTime));
}

// MANIPULATORS
inline
void Stopwatch::reset()
{
    d_isRunning             = false;
    d_accumulatedSystemTime = 0;
    d_accumulatedUserTime   = 0;
    d_accumulatedWallTime   = 0;
}

inline
void Stopwatch::start(bool collectCpuTimes)
{
    if (!d_isRunning) {
        d_collectCpuTimesFlag = collectCpuTimes;
        if (d_collectCpuTimesFlag) {
            accumulatedTimesRaw(&d_startSystemTime,
                                &d_startUserTime,
                                &d_startWallTime);
        }
        else {
            TimeUtil::getTimerRaw(&d_startWallTime);
        }
        d_isRunning = true;
    }
}

inline
void Stopwatch::stop()
{
    if (d_isRunning) {
        if (d_collectCpuTimesFlag) {
            updateTimes();
        }
        else {
            TimeUtil::OpaqueNativeTime now;
            TimeUtil::getTimerRaw(&now);
            d_accumulatedWallTime += elapsedWallTime(now);
        }
        d_isRunning = false;
    }
}

// ACCESSORS
inline
double Stopwatch::accumulatedSystemTime() const
{
    if (!d_collectCpuTimesFlag) {
        return 0.0;                                                   // RETURN
    }

    if (d_isRunning) {
        return (double)(d_accumulatedSystemTime
                  + TimeUtil::getProcessSystemTimer() - d_startSystemTime)
                                                      / s_nanosecondsPerSecond;
                                                                      // RETURN
    }
    return (double)d_accumulatedSystemTime / s_nanosecondsPerSecond;
}

inline
double Stopwatch::accumulatedUserTime() const
{
    if (!d_collectCpuTimesFlag) {
        return 0.0;                                                   // RETURN
    }

    if (d_isRunning) {
        return (double)(d_accumulatedUserTime
                      + TimeUtil::getProcessUserTimer() - d_startUserTime)
                                                      / s_nanosecondsPerSecond;
                                                                      // RETURN
    }
    return (double)d_accumulatedUserTime / s_nanosecondsPerSecond;
}

inline
double Stopwatch::accumulatedWallTime() const
{
    if (d_isRunning) {
        TimeUtil::OpaqueNativeTime now;
        TimeUtil::getTimerRaw(&now);
        return (double)(d_accumulatedWallTime + elapsedWallTime(now))
                                                      / s_nanosecondsPerSecond;
                                                                      // RETURN
    }
    return (double)d_accumulatedWallTime / s_nanosecondsPerSecond;
}

inline
double Stopwatch::elapsedTime() const
{
    return accumulatedWallTime();
}

inline
bool Stopwatch::isRunning() const
{
    return d_isRunning;
}

}  // close package namespace

#ifndef BDE_OPENSOURCE_PUBLICATION  // BACKWARD_COMPATIBILITY
// ============================================================================
//                           BACKWARD COMPATIBILITY
// ============================================================================

/// This alias is defined for backward compatibility.
typedef bsls::Stopwatch bsls_Stopwatch;
#endif  // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2016 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 ----------------------------------
