| 143 | |
| 144 | |
| 145 | Future<map<string, double>> MetricsProcess::snapshot( |
| 146 | const Option<Duration>& timeout) |
| 147 | { |
| 148 | // To avoid creating a new vector when calling `await()` below, we use three |
| 149 | // ordered vectors, where the Nth key in `keys` is associated with the Nth |
| 150 | // items in each of `futures` and `statistics`. |
| 151 | vector<string> keys; |
| 152 | vector<Future<double>> futures; |
| 153 | vector<Option<Statistics<double>>> statistics; |
| 154 | |
| 155 | keys.reserve(metrics.size()); |
| 156 | futures.reserve(metrics.size()); |
| 157 | statistics.reserve(metrics.size()); |
| 158 | |
| 159 | for (auto iter = metrics.begin(); iter != metrics.end(); ++iter) { |
| 160 | keys.emplace_back(iter->first); |
| 161 | futures.emplace_back(iter->second->value()); |
| 162 | statistics.emplace_back(iter->second->statistics()); |
| 163 | } |
| 164 | |
| 165 | Future<Nothing> timedout = |
| 166 | after(timeout.getOrElse(Duration::max())); |
| 167 | |
| 168 | // Return the response once it finishes or we time out. |
| 169 | // |
| 170 | // NOTE: We assign the result of `select()` to a local variable to ensure that |
| 171 | // the `await()` call in this expression is evaluated before the call to |
| 172 | // `std::move(futures)` in the subsequent expression. Otherwise, it's possible |
| 173 | // that the `move()` could be evaluated first, causing an empty vector to be |
| 174 | // passed into `await()`. |
| 175 | Future<Future<Nothing>> waited = |
| 176 | select<Nothing>({ |
| 177 | timedout, |
| 178 | await(futures).then([]{ return Nothing(); }) }); |
| 179 | |
| 180 | return waited |
| 181 | .onAny([=]() mutable { timedout.discard(); }) // Don't accumulate timers. |
| 182 | .then(defer(self(), |
| 183 | &Self::__snapshot, |
| 184 | timeout, |
| 185 | std::move(keys), |
| 186 | std::move(futures), |
| 187 | std::move(statistics))); |
| 188 | } |
| 189 | |
| 190 | |
| 191 | Future<http::Response> MetricsProcess::_snapshot( |