Thread-safe version of loopKeepAlive()
[folly.git] / folly / stats / BucketedTimeSeries-defs.h
index e9a749a7b43f21152b6f3d6d9bfaf2f757f7e501..bd38f6d5c8e6cb24043672945e5fa17f6dd437aa 100644 (file)
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef FOLLY_STATS_BUCKETEDTIMESERIES_INL_H_
-#define FOLLY_STATS_BUCKETEDTIMESERIES_INL_H_
+#pragma once
 
 #include <algorithm>
 #include <glog/logging.h>
 
 namespace folly {
 
-template <typename VT, typename TT>
-BucketedTimeSeries<VT, TT>::BucketedTimeSeries(size_t nBuckets,
-                                               TimeType maxDuration)
-  : firstTime_(1),
-    latestTime_(0),
-    duration_(maxDuration) {
+template <typename VT, typename CT>
+BucketedTimeSeries<VT, CT>::BucketedTimeSeries(
+    size_t nBuckets,
+    Duration maxDuration)
+    : firstTime_(Duration(1)), latestTime_(), duration_(maxDuration) {
   // For tracking all-time data we only use total_, and don't need to bother
   // with buckets_
   if (!isAllTime()) {
@@ -44,22 +42,24 @@ BucketedTimeSeries<VT, TT>::BucketedTimeSeries(size_t nBuckets,
   }
 }
 
-template <typename VT, typename TT>
-bool BucketedTimeSeries<VT, TT>::addValue(TimeType now, const ValueType& val) {
+template <typename VT, typename CT>
+bool BucketedTimeSeries<VT, CT>::addValue(TimePoint now, const ValueType& val) {
   return addValueAggregated(now, val, 1);
 }
 
-template <typename VT, typename TT>
-bool BucketedTimeSeries<VT, TT>::addValue(TimeType now,
-                                          const ValueType& val,
-                                          int64_t times) {
+template <typename VT, typename CT>
+bool BucketedTimeSeries<VT, CT>::addValue(
+    TimePoint now,
+    const ValueType& val,
+    int64_t times) {
   return addValueAggregated(now, val * times, times);
 }
 
-template <typename VT, typename TT>
-bool BucketedTimeSeries<VT, TT>::addValueAggregated(TimeType now,
-                                                    const ValueType& total,
-                                                    int64_t nsamples) {
+template <typename VT, typename CT>
+bool BucketedTimeSeries<VT, CT>::addValueAggregated(
+    TimePoint now,
+    const ValueType& total,
+    int64_t nsamples) {
   if (isAllTime()) {
     if (UNLIKELY(empty())) {
       firstTime_ = now;
@@ -99,8 +99,8 @@ bool BucketedTimeSeries<VT, TT>::addValueAggregated(TimeType now,
   return true;
 }
 
-template <typename VT, typename TT>
-size_t BucketedTimeSeries<VT, TT>::update(TimeType now) {
+template <typename VT, typename CT>
+size_t BucketedTimeSeries<VT, CT>::update(TimePoint now) {
   if (empty()) {
     // This is the first data point.
     firstTime_ = now;
@@ -122,8 +122,8 @@ size_t BucketedTimeSeries<VT, TT>::update(TimeType now) {
   return updateBuckets(now);
 }
 
-template <typename VT, typename TT>
-size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) {
+template <typename VT, typename CT>
+size_t BucketedTimeSeries<VT, CT>::updateBuckets(TimePoint now) {
   // We could cache nextBucketStart as a member variable, so we don't have to
   // recompute it each time update() is called with a new timestamp value.
   // This makes things faster when update() (or addValue()) is called once
@@ -134,8 +134,8 @@ size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) {
 
   // Get info about the bucket that latestTime_ points at
   size_t currentBucket;
-  TimeType currentBucketStart;
-  TimeType nextBucketStart;
+  TimePoint currentBucketStart;
+  TimePoint nextBucketStart;
   getBucketInfo(latestTime_, &currentBucket,
                 &currentBucketStart, &nextBucketStart);
 
@@ -173,30 +173,29 @@ size_t BucketedTimeSeries<VT, TT>::updateBuckets(TimeType now) {
   }
 }
 
-template <typename VT, typename TT>
-void BucketedTimeSeries<VT, TT>::clear() {
+template <typename VT, typename CT>
+void BucketedTimeSeries<VT, CT>::clear() {
   for (Bucket& bucket : buckets_) {
     bucket.clear();
   }
   total_.clear();
   // Set firstTime_ larger than latestTime_,
   // to indicate that the timeseries is empty
-  firstTime_ = TimeType(1);
-  latestTime_ = TimeType(0);
+  firstTime_ = TimePoint(Duration(1));
+  latestTime_ = TimePoint();
 }
 
-
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::getEarliestTime() const {
+template <typename VT, typename CT>
+typename CT::time_point BucketedTimeSeries<VT, CT>::getEarliestTime() const {
   if (empty()) {
-    return TimeType(0);
+    return TimePoint();
   }
   if (isAllTime()) {
     return firstTime_;
   }
 
   // Compute the earliest time we can track
-  TimeType earliestTime = getEarliestTimeNonEmpty();
+  TimePoint earliestTime = getEarliestTimeNonEmpty();
 
   // We're never tracking data before firstTime_
   earliestTime = std::max(earliestTime, firstTime_);
@@ -204,11 +203,12 @@ TT BucketedTimeSeries<VT, TT>::getEarliestTime() const {
   return earliestTime;
 }
 
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::getEarliestTimeNonEmpty() const {
+template <typename VT, typename CT>
+typename CT::time_point BucketedTimeSeries<VT, CT>::getEarliestTimeNonEmpty()
+    const {
   size_t currentBucket;
-  TimeType currentBucketStart;
-  TimeType nextBucketStart;
+  TimePoint currentBucketStart;
+  TimePoint nextBucketStart;
   getBucketInfo(latestTime_, &currentBucket,
                 &currentBucketStart, &nextBucketStart);
 
@@ -217,69 +217,82 @@ TT BucketedTimeSeries<VT, TT>::getEarliestTimeNonEmpty() const {
   return nextBucketStart - duration_;
 }
 
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::elapsed() const {
+template <typename VT, typename CT>
+typename CT::duration BucketedTimeSeries<VT, CT>::elapsed() const {
   if (empty()) {
-    return TimeType(0);
+    return Duration(0);
   }
 
   // Add 1 since [latestTime_, earliestTime] is an inclusive interval.
-  return latestTime_ - getEarliestTime() + TimeType(1);
+  return latestTime_ - getEarliestTime() + Duration(1);
 }
 
-template <typename VT, typename TT>
-TT BucketedTimeSeries<VT, TT>::elapsed(TimeType start, TimeType end) const {
+template <typename VT, typename CT>
+typename CT::duration BucketedTimeSeries<VT, CT>::elapsed(
+    TimePoint start,
+    TimePoint end) const {
   if (empty()) {
-    return TimeType(0);
+    return Duration(0);
   }
   start = std::max(start, getEarliestTime());
-  end = std::min(end, latestTime_ + TimeType(1));
+  end = std::min(end, latestTime_ + Duration(1));
   end = std::max(start, end);
   return end - start;
 }
 
-template <typename VT, typename TT>
-VT BucketedTimeSeries<VT, TT>::sum(TimeType start, TimeType end) const {
+template <typename VT, typename CT>
+VT BucketedTimeSeries<VT, CT>::sum(TimePoint start, TimePoint end) const {
   ValueType total = ValueType();
-  forEachBucket(start, end, [&](const Bucket& bucket,
-                                TimeType bucketStart,
-                                TimeType nextBucketStart) -> bool {
-    total += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
-                             bucket.sum);
-    return true;
-  });
+  forEachBucket(
+      start,
+      end,
+      [&](const Bucket& bucket,
+          TimePoint bucketStart,
+          TimePoint nextBucketStart) -> bool {
+        total += this->rangeAdjust(
+            bucketStart, nextBucketStart, start, end, bucket.sum);
+        return true;
+      });
 
   return total;
 }
 
-template <typename VT, typename TT>
-uint64_t BucketedTimeSeries<VT, TT>::count(TimeType start, TimeType end) const {
+template <typename VT, typename CT>
+uint64_t BucketedTimeSeries<VT, CT>::count(TimePoint start, TimePoint end)
+    const {
   uint64_t sample_count = 0;
-  forEachBucket(start, end, [&](const Bucket& bucket,
-                                TimeType bucketStart,
-                                TimeType nextBucketStart) -> bool {
-    sample_count += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
-                               bucket.count);
-    return true;
-  });
+  forEachBucket(
+      start,
+      end,
+      [&](const Bucket& bucket,
+          TimePoint bucketStart,
+          TimePoint nextBucketStart) -> bool {
+        sample_count += this->rangeAdjust(
+            bucketStart, nextBucketStart, start, end, bucket.count);
+        return true;
+      });
 
   return sample_count;
 }
 
-template <typename VT, typename TT>
+template <typename VT, typename CT>
 template <typename ReturnType>
-ReturnType BucketedTimeSeries<VT, TT>::avg(TimeType start, TimeType end) const {
+ReturnType BucketedTimeSeries<VT, CT>::avg(TimePoint start, TimePoint end)
+    const {
   ValueType total = ValueType();
   uint64_t sample_count = 0;
-  forEachBucket(start, end, [&](const Bucket& bucket,
-                                TimeType bucketStart,
-                                TimeType nextBucketStart) -> bool {
-    total += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
-                             bucket.sum);
-    sample_count += this->rangeAdjust(bucketStart, nextBucketStart, start, end,
-                               bucket.count);
-    return true;
-  });
+  forEachBucket(
+      start,
+      end,
+      [&](const Bucket& bucket,
+          TimePoint bucketStart,
+          TimePoint nextBucketStart) -> bool {
+        total += this->rangeAdjust(
+            bucketStart, nextBucketStart, start, end, bucket.sum);
+        sample_count += this->rangeAdjust(
+            bucketStart, nextBucketStart, start, end, bucket.count);
+        return true;
+      });
 
   if (sample_count == 0) {
     return ReturnType(0);
@@ -305,30 +318,32 @@ ReturnType BucketedTimeSeries<VT, TT>::avg(TimeType start, TimeType end) const {
  * into, we then divide by duration_.
  */
 
-template <typename VT, typename TT>
-size_t BucketedTimeSeries<VT, TT>::getBucketIdx(TimeType time) const {
+template <typename VT, typename CT>
+size_t BucketedTimeSeries<VT, CT>::getBucketIdx(TimePoint time) const {
   // For all-time data we don't use buckets_.  Everything is tracked in total_.
   DCHECK(!isAllTime());
 
-  time %= duration_;
-  return time.count() * buckets_.size() / duration_.count();
+  auto timeIntoCurrentCycle = (time.time_since_epoch() % duration_);
+  return timeIntoCurrentCycle.count() * buckets_.size() / duration_.count();
 }
 
 /*
  * Compute the bucket index for the specified time, as well as the earliest
  * time that falls into this bucket.
  */
-template <typename VT, typename TT>
-void BucketedTimeSeries<VT, TT>::getBucketInfo(
-    TimeType time, size_t *bucketIdx,
-    TimeType* bucketStart, TimeType* nextBucketStart) const {
-  typedef typename TimeType::rep TimeInt;
+template <typename VT, typename CT>
+void BucketedTimeSeries<VT, CT>::getBucketInfo(
+    TimePoint time,
+    size_t* bucketIdx,
+    TimePoint* bucketStart,
+    TimePoint* nextBucketStart) const {
+  typedef typename Duration::rep TimeInt;
   DCHECK(!isAllTime());
 
   // Keep these two lines together.  The compiler should be able to compute
   // both the division and modulus with a single operation.
-  TimeType timeMod = time % duration_;
-  TimeInt numFullDurations = time / duration_;
+  Duration timeMod = time.time_since_epoch() % duration_;
+  TimeInt numFullDurations = time.time_since_epoch() / duration_;
 
   TimeInt scaledTime = timeMod.count() * buckets_.size();
 
@@ -340,31 +355,31 @@ void BucketedTimeSeries<VT, TT>::getBucketInfo(
   TimeInt scaledBucketStart = scaledTime - scaledOffsetInBucket;
   TimeInt scaledNextBucketStart = scaledBucketStart + duration_.count();
 
-  TimeType bucketStartMod((scaledBucketStart + buckets_.size() - 1) /
-                          buckets_.size());
-  TimeType nextBucketStartMod((scaledNextBucketStart + buckets_.size() - 1) /
-                              buckets_.size());
+  Duration bucketStartMod(
+      (scaledBucketStart + buckets_.size() - 1) / buckets_.size());
+  Duration nextBucketStartMod(
+      (scaledNextBucketStart + buckets_.size() - 1) / buckets_.size());
 
-  TimeType durationStart(numFullDurations * duration_.count());
+  TimePoint durationStart(numFullDurations * duration_);
   *bucketStart = bucketStartMod + durationStart;
   *nextBucketStart = nextBucketStartMod + durationStart;
 }
 
-template <typename VT, typename TT>
+template <typename VT, typename CT>
 template <typename Function>
-void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const {
+void BucketedTimeSeries<VT, CT>::forEachBucket(Function fn) const {
   if (isAllTime()) {
-    fn(total_, firstTime_, latestTime_ + TimeType(1));
+    fn(total_, firstTime_, latestTime_ + Duration(1));
     return;
   }
 
-  typedef typename TimeType::rep TimeInt;
+  typedef typename Duration::rep TimeInt;
 
   // Compute durationStart, latestBucketIdx, and scaledNextBucketStart,
   // the same way as in getBucketInfo().
-  TimeType timeMod = latestTime_ % duration_;
-  TimeInt numFullDurations = latestTime_ / duration_;
-  TimeType durationStart(numFullDurations * duration_.count());
+  Duration timeMod = latestTime_.time_since_epoch() % duration_;
+  TimeInt numFullDurations = latestTime_.time_since_epoch() / duration_;
+  TimePoint durationStart(numFullDurations * duration_);
   TimeInt scaledTime = timeMod.count() * buckets_.size();
   size_t latestBucketIdx = scaledTime / duration_.count();
   TimeInt scaledOffsetInBucket = scaledTime % duration_.count();
@@ -377,9 +392,10 @@ void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const {
   size_t idx = latestBucketIdx;
   durationStart -= duration_;
 
-  TimeType nextBucketStart =
-    TimeType((scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()) +
-    durationStart;
+  TimePoint nextBucketStart =
+      Duration(
+          (scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()) +
+      durationStart;
   while (true) {
     ++idx;
     if (idx >= buckets_.size()) {
@@ -390,15 +406,19 @@ void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const {
       scaledNextBucketStart += duration_.count();
     }
 
-    TimeType bucketStart = nextBucketStart;
-    nextBucketStart = TimeType((scaledNextBucketStart + buckets_.size() - 1) /
-                               buckets_.size()) + durationStart;
+    TimePoint bucketStart = nextBucketStart;
+    nextBucketStart =
+        Duration(
+            (scaledNextBucketStart + buckets_.size() - 1) / buckets_.size()) +
+        durationStart;
 
     // Should we bother skipping buckets where firstTime_ >= nextBucketStart?
     // For now we go ahead and invoke the function with these buckets.
     // sum and count should always be 0 in these buckets.
 
-    DCHECK_LE(bucketStart.count(), latestTime_.count());
+    DCHECK_LE(
+        bucketStart.time_since_epoch().count(),
+        latestTime_.time_since_epoch().count());
     bool ret = fn(buckets_[idx], bucketStart, nextBucketStart);
     if (!ret) {
       break;
@@ -418,10 +438,13 @@ void BucketedTimeSeries<VT, TT>::forEachBucket(Function fn) const {
  * For example, if the bucket spans time [10, 20), but we only care about the
  * range [10, 16), this will return 60% of the input value.
  */
-template<typename VT, typename TT>
-VT BucketedTimeSeries<VT, TT>::rangeAdjust(
-    TimeType bucketStart, TimeType nextBucketStart,
-    TimeType start, TimeType end, ValueType input) const {
+template <typename VT, typename CT>
+VT BucketedTimeSeries<VT, CT>::rangeAdjust(
+    TimePoint bucketStart,
+    TimePoint nextBucketStart,
+    TimePoint start,
+    TimePoint end,
+    ValueType input) const {
   // If nextBucketStart is greater than latestTime_, treat nextBucketStart as
   // if it were latestTime_.  This makes us more accurate when someone is
   // querying for all of the data up to latestTime_.  Even though latestTime_
@@ -429,7 +452,7 @@ VT BucketedTimeSeries<VT, TT>::rangeAdjust(
   // downwards in this case, because the bucket really only has data up to
   // latestTime_.
   if (bucketStart <= latestTime_ && nextBucketStart > latestTime_) {
-    nextBucketStart = latestTime_ + TimeType(1);
+    nextBucketStart = latestTime_ + Duration(1);
   }
 
   if (start <= bucketStart && end >= nextBucketStart) {
@@ -437,29 +460,32 @@ VT BucketedTimeSeries<VT, TT>::rangeAdjust(
     return input;
   }
 
-  TimeType intervalStart = std::max(start, bucketStart);
-  TimeType intervalEnd = std::min(end, nextBucketStart);
+  TimePoint intervalStart = std::max(start, bucketStart);
+  TimePoint intervalEnd = std::min(end, nextBucketStart);
   return input * (intervalEnd - intervalStart) /
     (nextBucketStart - bucketStart);
 }
 
-template <typename VT, typename TT>
+template <typename VT, typename CT>
 template <typename Function>
-void BucketedTimeSeries<VT, TT>::forEachBucket(TimeType start, TimeType end,
-                                               Function fn) const {
-  forEachBucket([&start, &end, &fn] (const Bucket& bucket, TimeType bucketStart,
-                                     TimeType nextBucketStart) -> bool {
-    if (start >= nextBucketStart) {
-      return true;
-    }
-    if (end <= bucketStart) {
-      return false;
-    }
-    bool ret = fn(bucket, bucketStart, nextBucketStart);
-    return ret;
-  });
+void BucketedTimeSeries<VT, CT>::forEachBucket(
+    TimePoint start,
+    TimePoint end,
+    Function fn) const {
+  forEachBucket(
+      [&start, &end, &fn](
+          const Bucket& bucket,
+          TimePoint bucketStart,
+          TimePoint nextBucketStart) -> bool {
+        if (start >= nextBucketStart) {
+          return true;
+        }
+        if (end <= bucketStart) {
+          return false;
+        }
+        bool ret = fn(bucket, bucketStart, nextBucketStart);
+        return ret;
+      });
 }
 
 } // folly
-
-#endif // FOLLY_STATS_BUCKETEDTIMESERIES_INL_H_