1/*
2 * Copyright 2012-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <folly/SharedMutex.h>
20#include <folly/stats/detail/DigestBuilder.h>
21#include <folly/stats/detail/SlidingWindow.h>
22
23namespace folly {
24namespace detail {
25
26/*
27 * BufferedStat keeps a clock and every time period, will merge data from a
28 * DigestBuilder into a DigestT. Updates are made by the first appender after
29 * the expiry, or can be made at read time by calling update().
30 */
31template <typename DigestT, typename ClockT>
32class BufferedStat {
33 public:
34 using TimePoint = typename ClockT::time_point;
35
36 BufferedStat() = delete;
37
38 BufferedStat(
39 typename ClockT::duration bufferDuration,
40 size_t bufferSize,
41 size_t digestSize);
42
43 virtual ~BufferedStat() {}
44
45 void append(double value, TimePoint now = ClockT::now());
46
47 void flush();
48
49 protected:
50 // https://www.mail-archive.com/llvm-bugs@lists.llvm.org/msg18280.html
51 // Wrap the time point in something with a noexcept constructor.
52 struct TimePointHolder {
53 public:
54 TimePointHolder() noexcept {}
55
56 TimePointHolder(TimePoint t) : tp(t) {}
57
58 TimePoint tp;
59 };
60
61 const typename ClockT::duration bufferDuration_;
62 std::atomic<TimePointHolder> expiry_;
63 SharedMutex mutex_;
64
65 virtual void onNewDigest(
66 DigestT digest,
67 TimePoint newExpiry,
68 TimePoint oldExpiry,
69 const std::unique_lock<SharedMutex>& g) = 0;
70
71 // Update digest if now > expiry
72 std::unique_lock<SharedMutex> updateIfExpired(TimePoint now);
73
74 // Update digest unconditionally
75 std::unique_lock<SharedMutex> update();
76
77 private:
78 DigestBuilder<DigestT> digestBuilder_;
79
80 // Controls how digest updates happen in doUpdate
81 enum class UpdateMode {
82 OnExpiry,
83 Now,
84 };
85
86 // Update digest. If updateMode == UpdateMode::Now digest is updated
87 // unconditionally, else digest is updated only if expiry has passed.
88 void doUpdate(
89 TimePoint now,
90 const std::unique_lock<SharedMutex>& g,
91 UpdateMode updateMode);
92
93 TimePoint roundUp(TimePoint t);
94};
95
96/*
97 * BufferedDigest is a BufferedStat that holds data in a single digest.
98 */
99template <typename DigestT, typename ClockT>
100class BufferedDigest : public BufferedStat<DigestT, ClockT> {
101 public:
102 using TimePoint = typename ClockT::time_point;
103
104 BufferedDigest(
105 typename ClockT::duration bufferDuration,
106 size_t bufferSize,
107 size_t digestSize);
108
109 DigestT get(TimePoint now = ClockT::now());
110
111 void onNewDigest(
112 DigestT digest,
113 TimePoint newExpiry,
114 TimePoint oldExpiry,
115 const std::unique_lock<SharedMutex>& g) final;
116
117 private:
118 DigestT digest_;
119};
120
121/*
122 * BufferedSlidingWindow is a BufferedStat that holds data in a SlidingWindow.
123 * onBufferSwap will slide the SlidingWindow and return the front of the list.
124 */
125template <typename DigestT, typename ClockT>
126class BufferedSlidingWindow : public BufferedStat<DigestT, ClockT> {
127 public:
128 using TimePoint = typename ClockT::time_point;
129
130 BufferedSlidingWindow(
131 size_t nBuckets,
132 typename ClockT::duration bufferDuration,
133 size_t bufferSize,
134 size_t digestSize);
135
136 std::vector<DigestT> get(TimePoint now = ClockT::now());
137
138 void onNewDigest(
139 DigestT digest,
140 TimePoint newExpiry,
141 TimePoint oldExpiry,
142 const std::unique_lock<SharedMutex>& g) final;
143
144 private:
145 SlidingWindow<DigestT> slidingWindow_;
146};
147
148} // namespace detail
149} // namespace folly
150