blob: 6861c7847a1a09f992487f9fb6ba59dfcdd1af96 [file] [log] [blame]
Andy Hung06f3aba2019-12-03 16:36:42 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
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 <any>
20#include <map>
21#include <sstream>
22#include <string>
23#include <variant>
24#include <vector>
25
Andy Hungf7c14102020-04-18 14:54:08 -070026#include <android-base/thread_annotations.h>
Ray Essickf27e9872019-12-07 06:28:46 -080027#include <media/MediaMetricsItem.h>
Andy Hung06f3aba2019-12-03 16:36:42 -080028#include <utils/Timers.h>
29
30namespace android::mediametrics {
31
32// define a way of printing the monostate
33inline std::ostream & operator<< (std::ostream& s,
34 std::monostate const& v __unused) {
35 s << "none_item";
36 return s;
37}
38
Andy Hunge8989ae2020-01-03 12:20:43 -080039// define a way of printing a std::pair.
40template <typename T, typename U>
41std::ostream & operator<< (std::ostream& s,
42 const std::pair<T, U>& v) {
43 s << "{ " << v.first << ", " << v.second << " }";
44 return s;
45}
46
Andy Hung06f3aba2019-12-03 16:36:42 -080047// define a way of printing a variant
48// see https://en.cppreference.com/w/cpp/utility/variant/visit
49template <typename T0, typename ... Ts>
50std::ostream & operator<< (std::ostream& s,
51 std::variant<T0, Ts...> const& v) {
52 std::visit([&s](auto && arg){ s << std::forward<decltype(arg)>(arg); }, v);
53 return s;
54}
55
56/**
57 * The TimeMachine is used to record timing changes of MediaAnalyticItem
58 * properties.
59 *
Andy Hung8f069622020-02-10 15:44:01 -080060 * Any URL that ends with '#' (AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED)
61 * will have a time sequence that keeps duplicates.
Andy Hung06f3aba2019-12-03 16:36:42 -080062 *
63 * The TimeMachine is NOT thread safe.
64 */
Andy Hunga8f1c6e2020-01-02 18:25:41 -080065class TimeMachine final { // made final as we have copy constructor instead of dup() override.
Andy Hunge8989ae2020-01-03 12:20:43 -080066public:
67 using Elem = Item::Prop::Elem; // use the Item property element.
Andy Hung06f3aba2019-12-03 16:36:42 -080068 using PropertyHistory = std::multimap<int64_t /* time */, Elem>;
69
Andy Hunge8989ae2020-01-03 12:20:43 -080070private:
71
Andy Hung06f3aba2019-12-03 16:36:42 -080072 // KeyHistory contains no lock.
73 // Access is through the TimeMachine, and a hash-striped lock is used
74 // before calling into KeyHistory.
75 class KeyHistory {
76 public:
77 template <typename T>
78 KeyHistory(T key, pid_t pid, uid_t uid, int64_t time)
79 : mKey(key)
80 , mPid(pid)
81 , mUid(uid)
82 , mCreationTime(time)
83 , mLastModificationTime(time)
84 {
Andy Hung611268d2019-12-19 13:54:02 -080085 putValue(BUNDLE_PID, (int32_t)pid, time);
86 putValue(BUNDLE_UID, (int32_t)uid, time);
Andy Hung06f3aba2019-12-03 16:36:42 -080087 }
88
Andy Hunga8f1c6e2020-01-02 18:25:41 -080089 KeyHistory(const KeyHistory &other) = default;
90
Andy Hung06f3aba2019-12-03 16:36:42 -080091 status_t checkPermission(uid_t uidCheck) const {
92 return uidCheck != (uid_t)-1 && uidCheck != mUid ? PERMISSION_DENIED : NO_ERROR;
93 }
94
95 template <typename T>
Andy Hungf7c14102020-04-18 14:54:08 -070096 status_t getValue(const std::string &property, T* value, int64_t time = 0) const
97 REQUIRES(mPseudoKeyHistoryLock) {
Andy Hunged416da2020-03-05 18:42:55 -080098 if (time == 0) time = systemTime(SYSTEM_TIME_REALTIME);
Andy Hung06f3aba2019-12-03 16:36:42 -080099 const auto tsptr = mPropertyMap.find(property);
100 if (tsptr == mPropertyMap.end()) return BAD_VALUE;
101 const auto& timeSequence = tsptr->second;
102 auto eptr = timeSequence.upper_bound(time);
103 if (eptr == timeSequence.begin()) return BAD_VALUE;
104 --eptr;
105 if (eptr == timeSequence.end()) return BAD_VALUE;
106 const T* vptr = std::get_if<T>(&eptr->second);
107 if (vptr == nullptr) return BAD_VALUE;
108 *value = *vptr;
109 return NO_ERROR;
110 }
111
112 template <typename T>
Andy Hungf7c14102020-04-18 14:54:08 -0700113 status_t getValue(const std::string &property, T defaultValue, int64_t time = 0) const
114 REQUIRES(mPseudoKeyHistoryLock){
Andy Hung06f3aba2019-12-03 16:36:42 -0800115 T value;
116 return getValue(property, &value, time) != NO_ERROR ? defaultValue : value;
117 }
118
119 void putProp(
Andy Hungf7c14102020-04-18 14:54:08 -0700120 const std::string &name, const mediametrics::Item::Prop &prop, int64_t time = 0)
121 REQUIRES(mPseudoKeyHistoryLock) {
Andy Hunge8989ae2020-01-03 12:20:43 -0800122 //alternatively: prop.visit([&](auto value) { putValue(name, value, time); });
123 putValue(name, prop.get(), time);
Andy Hung06f3aba2019-12-03 16:36:42 -0800124 }
125
126 template <typename T>
Andy Hungf7c14102020-04-18 14:54:08 -0700127 void putValue(const std::string &property, T&& e, int64_t time = 0)
128 REQUIRES(mPseudoKeyHistoryLock) {
Andy Hunged416da2020-03-05 18:42:55 -0800129 if (time == 0) time = systemTime(SYSTEM_TIME_REALTIME);
Andy Hung06f3aba2019-12-03 16:36:42 -0800130 mLastModificationTime = time;
Andy Hung5d3f2d12020-03-04 19:55:03 -0800131 if (mPropertyMap.size() >= kKeyMaxProperties &&
132 !mPropertyMap.count(property)) {
133 ALOGV("%s: too many properties, rejecting %s", __func__, property.c_str());
134 return;
135 }
Andy Hung06f3aba2019-12-03 16:36:42 -0800136 auto& timeSequence = mPropertyMap[property];
137 Elem el{std::forward<T>(e)};
138 if (timeSequence.empty() // no elements
Andy Hung8f069622020-02-10 15:44:01 -0800139 || property.back() == AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED
Andy Hung06f3aba2019-12-03 16:36:42 -0800140 || timeSequence.rbegin()->second != el) { // value changed
Andy Hung7d391082020-04-18 15:03:51 -0700141 timeSequence.emplace_hint(timeSequence.end(), time, std::move(el));
Andy Hung5d3f2d12020-03-04 19:55:03 -0800142
143 if (timeSequence.size() > kTimeSequenceMaxElements) {
144 ALOGV("%s: restricting maximum elements (discarding oldest) for %s",
145 __func__, property.c_str());
146 timeSequence.erase(timeSequence.begin());
147 }
Andy Hung06f3aba2019-12-03 16:36:42 -0800148 }
149 }
150
Andy Hungf7c14102020-04-18 14:54:08 -0700151 std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const
152 REQUIRES(mPseudoKeyHistoryLock) {
Andy Hung06f3aba2019-12-03 16:36:42 -0800153 std::stringstream ss;
154 int32_t ll = lines;
155 for (auto& tsPair : mPropertyMap) {
156 if (ll <= 0) break;
Andy Hung709b91e2020-04-04 14:23:36 -0700157 std::string s = dump(mKey, tsPair, time);
158 if (s.size() > 0) {
159 --ll;
Andy Hungb744faf2020-04-09 13:09:26 -0700160 ss << s;
Andy Hung709b91e2020-04-04 14:23:36 -0700161 }
Andy Hung06f3aba2019-12-03 16:36:42 -0800162 }
163 return { ss.str(), lines - ll };
164 }
165
Andy Hungf7c14102020-04-18 14:54:08 -0700166 int64_t getLastModificationTime() const REQUIRES(mPseudoKeyHistoryLock) {
167 return mLastModificationTime;
168 }
Andy Hung06f3aba2019-12-03 16:36:42 -0800169
170 private:
171 static std::string dump(
172 const std::string &key,
173 const std::pair<std::string /* prop */, PropertyHistory>& tsPair,
174 int64_t time) {
175 const auto timeSequence = tsPair.second;
176 auto eptr = timeSequence.lower_bound(time);
177 if (eptr == timeSequence.end()) {
Andy Hung709b91e2020-04-04 14:23:36 -0700178 return {}; // don't dump anything. tsPair.first + "={};\n";
Andy Hung06f3aba2019-12-03 16:36:42 -0800179 }
180 std::stringstream ss;
181 ss << key << "." << tsPair.first << "={";
Andy Hung3b4c1f02020-01-23 18:58:32 -0800182
183 time_string_t last_timestring{}; // last timestring used.
184 while (true) {
185 const time_string_t timestring = mediametrics::timeStringFromNs(eptr->first);
186 // find common prefix offset.
187 const size_t offset = commonTimePrefixPosition(timestring.time,
188 last_timestring.time);
189 last_timestring = timestring;
190 ss << "(" << (offset == 0 ? "" : "~") << &timestring.time[offset]
191 << ") " << eptr->second;
192 if (++eptr == timeSequence.end()) {
Andy Hung3b4c1f02020-01-23 18:58:32 -0800193 break;
194 }
195 ss << ", ";
196 }
Andy Hung06f3aba2019-12-03 16:36:42 -0800197 ss << "};\n";
198 return ss.str();
199 }
200
201 const std::string mKey;
202 const pid_t mPid __unused;
203 const uid_t mUid;
204 const int64_t mCreationTime __unused;
205
206 int64_t mLastModificationTime;
207 std::map<std::string /* property */, PropertyHistory> mPropertyMap;
208 };
209
210 using History = std::map<std::string /* key */, std::shared_ptr<KeyHistory>>;
211
Andy Hung5d3f2d12020-03-04 19:55:03 -0800212 static inline constexpr size_t kTimeSequenceMaxElements = 100;
213 static inline constexpr size_t kKeyMaxProperties = 100;
Andy Hung06f3aba2019-12-03 16:36:42 -0800214 static inline constexpr size_t kKeyLowWaterMark = 500;
215 static inline constexpr size_t kKeyHighWaterMark = 1000;
216
217 // Estimated max data space usage is 3KB * kKeyHighWaterMark.
218
219public:
220
221 TimeMachine() = default;
222 TimeMachine(size_t keyLowWaterMark, size_t keyHighWaterMark)
223 : mKeyLowWaterMark(keyLowWaterMark)
224 , mKeyHighWaterMark(keyHighWaterMark) {
225 LOG_ALWAYS_FATAL_IF(keyHighWaterMark <= keyLowWaterMark,
226 "%s: required that keyHighWaterMark:%zu > keyLowWaterMark:%zu",
227 __func__, keyHighWaterMark, keyLowWaterMark);
228 }
229
Andy Hunga8f1c6e2020-01-02 18:25:41 -0800230 // The TimeMachine copy constructor/assignment uses a deep copy,
231 // though the snapshot is not instantaneous nor isochronous.
232 //
233 // If there are concurrent operations ongoing in the other TimeMachine
234 // then there may be some history more recent than others (a time shear).
235 // This is expected to be a benign addition in history as small number of
236 // future elements are incorporated.
237 TimeMachine(const TimeMachine& other) {
238 *this = other;
239 }
240 TimeMachine& operator=(const TimeMachine& other) {
241 std::lock_guard lock(mLock);
242 mHistory.clear();
243
244 {
245 std::lock_guard lock2(other.mLock);
246 mHistory = other.mHistory;
247 }
248
249 // Now that we safely have our own shared pointers, let's dup them
250 // to ensure they are decoupled. We do this by acquiring the other lock.
251 for (const auto &[lkey, lhist] : mHistory) {
252 std::lock_guard lock2(other.getLockForKey(lkey));
253 mHistory[lkey] = std::make_shared<KeyHistory>(*lhist);
254 }
255 return *this;
256 }
257
Andy Hung06f3aba2019-12-03 16:36:42 -0800258 /**
259 * Put all the properties from an item into the Time Machine log.
260 */
Ray Essickf27e9872019-12-07 06:28:46 -0800261 status_t put(const std::shared_ptr<const mediametrics::Item>& item, bool isTrusted = false) {
Andy Hung06f3aba2019-12-03 16:36:42 -0800262 const int64_t time = item->getTimestamp();
263 const std::string &key = item->getKey();
264
Andy Hung5d3f2d12020-03-04 19:55:03 -0800265 ALOGV("%s(%zu, %zu): key: %s isTrusted:%d size:%zu",
266 __func__, mKeyLowWaterMark, mKeyHighWaterMark,
267 key.c_str(), (int)isTrusted, item->count());
Andy Hung06f3aba2019-12-03 16:36:42 -0800268 std::shared_ptr<KeyHistory> keyHistory;
269 {
270 std::vector<std::any> garbage;
271 std::lock_guard lock(mLock);
272
273 auto it = mHistory.find(key);
274 if (it == mHistory.end()) {
275 if (!isTrusted) return PERMISSION_DENIED;
276
Andy Hungf7c14102020-04-18 14:54:08 -0700277 (void)gc(garbage);
Andy Hung06f3aba2019-12-03 16:36:42 -0800278
279 // no keylock needed here as we are sole owner
280 // until placed on mHistory.
281 keyHistory = std::make_shared<KeyHistory>(
282 key, item->getPid(), item->getUid(), time);
283 mHistory[key] = keyHistory;
284 } else {
285 keyHistory = it->second;
286 }
287 }
288
289 // deferred contains remote properties (for other keys) to do later.
Ray Essickf27e9872019-12-07 06:28:46 -0800290 std::vector<const mediametrics::Item::Prop *> deferred;
Andy Hung06f3aba2019-12-03 16:36:42 -0800291 {
292 // handle local properties
293 std::lock_guard lock(getLockForKey(key));
294 if (!isTrusted) {
295 status_t status = keyHistory->checkPermission(item->getUid());
296 if (status != NO_ERROR) return status;
297 }
298
299 for (const auto &prop : *item) {
300 const std::string &name = prop.getName();
301 if (name.size() == 0 || name[0] == '_') continue;
302
303 // Cross key settings are with [key]property
304 if (name[0] == '[') {
305 if (!isTrusted) continue;
306 deferred.push_back(&prop);
307 } else {
308 keyHistory->putProp(name, prop, time);
309 }
310 }
311 }
312
313 // handle remote properties, if any
314 for (const auto propptr : deferred) {
315 const auto &prop = *propptr;
316 const std::string &name = prop.getName();
317 size_t end = name.find_first_of(']'); // TODO: handle nested [] or escape?
318 if (end == 0) continue;
319 std::string remoteKey = name.substr(1, end - 1);
320 std::string remoteName = name.substr(end + 1);
321 if (remoteKey.size() == 0 || remoteName.size() == 0) continue;
322 std::shared_ptr<KeyHistory> remoteKeyHistory;
323 {
324 std::lock_guard lock(mLock);
325 auto it = mHistory.find(remoteKey);
326 if (it == mHistory.end()) continue;
327 remoteKeyHistory = it->second;
328 }
Andy Hungb744faf2020-04-09 13:09:26 -0700329 std::lock_guard lock(getLockForKey(remoteKey));
Andy Hung06f3aba2019-12-03 16:36:42 -0800330 remoteKeyHistory->putProp(remoteName, prop, time);
331 }
332 return NO_ERROR;
333 }
334
335 template <typename T>
336 status_t get(const std::string &key, const std::string &property,
337 T* value, int32_t uidCheck = -1, int64_t time = 0) const {
338 std::shared_ptr<KeyHistory> keyHistory;
339 {
340 std::lock_guard lock(mLock);
341 const auto it = mHistory.find(key);
342 if (it == mHistory.end()) return BAD_VALUE;
343 keyHistory = it->second;
344 }
345 std::lock_guard lock(getLockForKey(key));
346 return keyHistory->checkPermission(uidCheck)
347 ?: keyHistory->getValue(property, value, time);
348 }
349
350 /**
351 * Individual property put.
352 *
Andy Hunged416da2020-03-05 18:42:55 -0800353 * Put takes in a time (if none is provided then SYSTEM_TIME_REALTIME is used).
Andy Hung06f3aba2019-12-03 16:36:42 -0800354 */
355 template <typename T>
356 status_t put(const std::string &url, T &&e, int64_t time = 0) {
357 std::string key;
358 std::string prop;
359 std::shared_ptr<KeyHistory> keyHistory =
360 getKeyHistoryFromUrl(url, &key, &prop);
361 if (keyHistory == nullptr) return BAD_VALUE;
Andy Hunged416da2020-03-05 18:42:55 -0800362 if (time == 0) time = systemTime(SYSTEM_TIME_REALTIME);
Andy Hung06f3aba2019-12-03 16:36:42 -0800363 std::lock_guard lock(getLockForKey(key));
364 keyHistory->putValue(prop, std::forward<T>(e), time);
365 return NO_ERROR;
366 }
367
368 /**
369 * Individual property get
370 */
371 template <typename T>
372 status_t get(const std::string &url, T* value, int32_t uidCheck, int64_t time = 0) const {
373 std::string key;
374 std::string prop;
375 std::shared_ptr<KeyHistory> keyHistory =
376 getKeyHistoryFromUrl(url, &key, &prop);
377 if (keyHistory == nullptr) return BAD_VALUE;
378
379 std::lock_guard lock(getLockForKey(key));
380 return keyHistory->checkPermission(uidCheck)
381 ?: keyHistory->getValue(prop, value, time);
382 }
383
384 /**
385 * Individual property get with default
386 */
387 template <typename T>
388 T get(const std::string &url, const T &defaultValue, int32_t uidCheck,
389 int64_t time = 0) const {
390 T value;
391 return get(url, &value, uidCheck, time) == NO_ERROR
392 ? value : defaultValue;
393 }
394
395 /**
396 * Returns number of keys in the Time Machine.
397 */
398 size_t size() const {
399 std::lock_guard lock(mLock);
400 return mHistory.size();
401 }
402
403 /**
404 * Clears all properties from the Time Machine.
405 */
406 void clear() {
407 std::lock_guard lock(mLock);
408 mHistory.clear();
409 }
410
411 /**
412 * Returns a pair consisting of the TimeMachine state as a string
413 * and the number of lines in the string.
414 *
415 * The number of lines in the returned pair is used as an optimization
416 * for subsequent line limiting.
417 *
418 * \param lines the maximum number of lines in the string returned.
419 * \param key selects only that key.
Andy Hung709b91e2020-04-04 14:23:36 -0700420 * \param sinceNs the nanoseconds since Unix epoch to start dump (0 shows all)
421 * \param prefix the desired key prefix to match (nullptr shows all)
Andy Hung06f3aba2019-12-03 16:36:42 -0800422 */
423 std::pair<std::string, int32_t> dump(
Andy Hung709b91e2020-04-04 14:23:36 -0700424 int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const {
Andy Hung06f3aba2019-12-03 16:36:42 -0800425 std::lock_guard lock(mLock);
Andy Hung06f3aba2019-12-03 16:36:42 -0800426 std::stringstream ss;
427 int32_t ll = lines;
Andy Hung709b91e2020-04-04 14:23:36 -0700428
429 for (auto it = prefix != nullptr ? mHistory.lower_bound(prefix) : mHistory.begin();
430 it != mHistory.end();
431 ++it) {
432 if (ll <= 0) break;
433 if (prefix != nullptr && !startsWith(it->first, prefix)) break;
434 std::lock_guard lock(getLockForKey(it->first));
435 auto [s, l] = it->second->dump(ll, sinceNs);
Andy Hungb744faf2020-04-09 13:09:26 -0700436 ss << s;
Andy Hung06f3aba2019-12-03 16:36:42 -0800437 ll -= l;
438 }
439 return { ss.str(), lines - ll };
440 }
441
442private:
443
444 // Obtains the lock for a KeyHistory.
Andy Hungf7c14102020-04-18 14:54:08 -0700445 std::mutex &getLockForKey(const std::string &key) const
446 RETURN_CAPABILITY(mPseudoKeyHistoryLock) {
Andy Hung06f3aba2019-12-03 16:36:42 -0800447 return mKeyLocks[std::hash<std::string>{}(key) % std::size(mKeyLocks)];
448 }
449
450 // Finds a KeyHistory from a URL. Returns nullptr if not found.
451 std::shared_ptr<KeyHistory> getKeyHistoryFromUrl(
Andy Hungb744faf2020-04-09 13:09:26 -0700452 const std::string& url, std::string* key, std::string *prop) const {
Andy Hung06f3aba2019-12-03 16:36:42 -0800453 std::lock_guard lock(mLock);
454
455 auto it = mHistory.upper_bound(url);
456 if (it == mHistory.begin()) {
457 return nullptr;
458 }
459 --it; // go to the actual key, if it exists.
460
461 const std::string& itKey = it->first;
462 if (strncmp(itKey.c_str(), url.c_str(), itKey.size())) {
463 return nullptr;
464 }
465 if (key) *key = itKey;
466 if (prop) *prop = url.substr(itKey.size() + 1);
467 return it->second;
468 }
469
Andy Hung06f3aba2019-12-03 16:36:42 -0800470 /**
471 * Garbage collects if the TimeMachine size exceeds the high water mark.
472 *
Andy Hung5d3f2d12020-03-04 19:55:03 -0800473 * This GC operation limits the number of keys stored (not the size of properties
474 * stored in each key).
475 *
Andy Hung06f3aba2019-12-03 16:36:42 -0800476 * \param garbage a type-erased vector of elements to be destroyed
477 * outside of lock. Move large items to be destroyed here.
478 *
479 * \return true if garbage collection was done.
480 */
Andy Hungf7c14102020-04-18 14:54:08 -0700481 bool gc(std::vector<std::any>& garbage) REQUIRES(mLock) {
Andy Hung06f3aba2019-12-03 16:36:42 -0800482 // TODO: something better than this for garbage collection.
483 if (mHistory.size() < mKeyHighWaterMark) return false;
484
485 ALOGD("%s: garbage collection", __func__);
486
487 // erase everything explicitly expired.
488 std::multimap<int64_t, std::string> accessList;
489 // use a stale vector with precise type to avoid type erasure overhead in garbage
490 std::vector<std::shared_ptr<KeyHistory>> stale;
491
492 for (auto it = mHistory.begin(); it != mHistory.end();) {
493 const std::string& key = it->first;
494 std::shared_ptr<KeyHistory> &keyHist = it->second;
495
496 std::lock_guard lock(getLockForKey(it->first));
497 int64_t expireTime = keyHist->getValue("_expire", -1 /* default */);
498 if (expireTime != -1) {
499 stale.emplace_back(std::move(it->second));
500 it = mHistory.erase(it);
501 } else {
502 accessList.emplace(keyHist->getLastModificationTime(), key);
503 ++it;
504 }
505 }
506
507 if (mHistory.size() > mKeyLowWaterMark) {
508 const size_t toDelete = mHistory.size() - mKeyLowWaterMark;
509 auto it = accessList.begin();
510 for (size_t i = 0; i < toDelete; ++i) {
511 auto it2 = mHistory.find(it->second);
512 stale.emplace_back(std::move(it2->second));
513 mHistory.erase(it2);
514 ++it;
515 }
516 }
517 garbage.emplace_back(std::move(accessList));
518 garbage.emplace_back(std::move(stale));
519
520 ALOGD("%s(%zu, %zu): key size:%zu",
521 __func__, mKeyLowWaterMark, mKeyHighWaterMark,
522 mHistory.size());
523 return true;
524 }
525
526 const size_t mKeyLowWaterMark = kKeyLowWaterMark;
527 const size_t mKeyHighWaterMark = kKeyHighWaterMark;
528
529 /**
530 * Locking Strategy
531 *
532 * Each key in the History has a KeyHistory. To get a shared pointer to
533 * the KeyHistory requires a lookup of mHistory under mLock. Once the shared
534 * pointer to KeyHistory is obtained, the mLock for mHistory can be released.
535 *
536 * Once the shared pointer to the key's KeyHistory is obtained, the KeyHistory
537 * can be locked for read and modification through the method getLockForKey().
538 *
539 * Instead of having a mutex per KeyHistory, we use a hash striped lock
540 * which assigns a mutex based on the hash of the key string.
541 *
542 * Once the last shared pointer reference to KeyHistory is released, it is
543 * destroyed. This is done through the garbage collection method.
544 *
545 * This two level locking allows multiple threads to access the TimeMachine
546 * in parallel.
547 */
548
549 mutable std::mutex mLock; // Lock for mHistory
Andy Hungf7c14102020-04-18 14:54:08 -0700550 History mHistory GUARDED_BY(mLock);
Andy Hung06f3aba2019-12-03 16:36:42 -0800551
552 // KEY_LOCKS is the number of mutexes for keys.
553 // It need not be a power of 2, but faster that way.
554 static inline constexpr size_t KEY_LOCKS = 256;
555 mutable std::mutex mKeyLocks[KEY_LOCKS]; // Hash-striped lock for KeyHistory based on key.
Andy Hungf7c14102020-04-18 14:54:08 -0700556
557 // Used for thread-safety analysis, we create a fake mutex object to represent
558 // the hash stripe lock mechanism, which is then tracked by the compiler.
559 class CAPABILITY("mutex") PseudoLock {};
560 static inline PseudoLock mPseudoKeyHistoryLock;
Andy Hung06f3aba2019-12-03 16:36:42 -0800561};
562
563} // namespace android::mediametrics