From aee3d1a322d14c4d573d247dac3181d66b346f0f Mon Sep 17 00:00:00 2001
From: Darren Hsu <darrenhsu@google.com>
Date: Mon, 28 Mar 2022 14:26:25 +0800
Subject: [PATCH] powerstats: timeout mechanism for AoC data provider

Bug: 219630658
Test: dumpsys android.hardware.power.stats.IPowerStats/default
Change-Id: Ib70000580f9515b3cb73968dd190f99ae8af178f
Signed-off-by: Darren Hsu <darrenhsu@google.com>
---
 powerstats/AocStateResidencyDataProvider.cpp  | 95 +++++++++++++++++--
 .../include/AocStateResidencyDataProvider.h   |  7 +-
 2 files changed, 93 insertions(+), 9 deletions(-)

diff --git a/powerstats/AocStateResidencyDataProvider.cpp b/powerstats/AocStateResidencyDataProvider.cpp
index c64496d..b8d40ea 100644
--- a/powerstats/AocStateResidencyDataProvider.cpp
+++ b/powerstats/AocStateResidencyDataProvider.cpp
@@ -17,6 +17,8 @@
 #include "AocStateResidencyDataProvider.h"
 
 #include <android-base/logging.h>
+#include <chrono>
+#include <pthread.h>
 
 namespace aidl {
 namespace android {
@@ -24,12 +26,22 @@ namespace hardware {
 namespace power {
 namespace stats {
 
+struct async_data_t {
+    pthread_cond_t *cond;
+    pthread_mutex_t *lock;
+    std::unordered_map<std::string, std::vector<StateResidency>> *residencies;
+    std::unordered_map<std::string,
+        std::vector<std::unique_ptr<GenericStateResidencyDataProvider>>> *providers;
+};
+
 AocStateResidencyDataProvider::AocStateResidencyDataProvider(std::vector<std::pair<std::string,
-        std::string>> ids, std::vector<std::pair<std::string, std::string>> states) {
+        std::string>> ids, std::vector<std::pair<std::string, std::string>> states,
+        const uint64_t timeoutMillis) {
     // AoC stats are reported in ticks of 244.140625ns. The transform
     // function converts ticks to milliseconds.
     // 1000000 / 244.140625 = 4096.
     static const uint64_t AOC_CLK = 4096;
+    static const uint64_t TIMEOUT_MILLIS = 120;
     std::function<uint64_t(uint64_t)> aocTickToMs = [](uint64_t a) { return a / AOC_CLK; };
     GenericStateResidencyDataProvider::StateResidencyConfig config = {
             .entryCountSupported = true,
@@ -54,13 +66,16 @@ AocStateResidencyDataProvider::AocStateResidencyDataProvider(std::vector<std::pa
             mProviders[id.first].push_back(std::move(sdp));
         }
     }
+    mStateCount = states.size();
+    mTimeoutMillis = timeoutMillis == 0 ? TIMEOUT_MILLIS : timeoutMillis;
 }
 
-bool AocStateResidencyDataProvider::getStateResidencies(
-        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
+void *getStateResidenciesAsync(void *arg) {
+    struct async_data_t *async = (struct async_data_t*)arg;
+    const auto *originalContainer = async->residencies;
+
     // States from the same power entity are merged.
-    bool ret = true;
-    for (const auto &providerList : mProviders) {
+    for (const auto &providerList : *async->providers) {
         int32_t stateId = 0;
         std::string curEntity = providerList.first;
         std::vector<StateResidency> stateResidencies;
@@ -68,7 +83,7 @@ bool AocStateResidencyDataProvider::getStateResidencies(
         // Iterate over each provider in the providerList, appending each of the states
         for (const auto &provider : providerList.second) {
             std::unordered_map<std::string, std::vector<StateResidency>> residency;
-            ret &= provider->getStateResidencies(&residency);
+            provider->getStateResidencies(&residency);
 
             // Each provider should only return data for curEntity but checking anyway
             if (residency.find(curEntity) != residency.end()) {
@@ -84,8 +99,74 @@ bool AocStateResidencyDataProvider::getStateResidencies(
             }
         }
 
-        residencies->emplace(curEntity, stateResidencies);
+        if (async->residencies != originalContainer) {
+            /*
+             * When provider->gretStateResidencies() exceeds timeout expiration, the main thread
+             * stops waiting and continue, then the original residency pointer is erased in the
+             * main thread when AocStateResidencyDataProvider::getStateResidencies() ends, and new
+             * container will be created in the async thread later. In this situation, we ignore
+             * delayed residency data and end the async thread.
+             */
+            return 0;
+        }
+
+        async->residencies->emplace(curEntity, stateResidencies);
     }
+
+    pthread_mutex_lock(async->lock);
+    pthread_cond_signal(async->cond);
+    pthread_mutex_unlock(async->lock);
+
+    return 0;
+}
+
+bool AocStateResidencyDataProvider::getStateResidencies(
+        std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
+    bool ret = true;
+    int condResult = 0;
+    pthread_t tid;
+    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+    std::unordered_map<std::string, std::vector<StateResidency>> stateResidencies;
+    struct timespec start, timeout;
+    struct async_data_t async = {
+        .cond = &cond,
+        .lock = &lock,
+        .residencies = &stateResidencies,
+        .providers = &mProviders
+    };
+
+    pthread_create(&tid, NULL, &getStateResidenciesAsync, (void*)&async);
+
+    clock_gettime(CLOCK_REALTIME, &start);
+
+    uint64_t expirationMillis = mTimeoutMillis * mStateCount;
+    timeout.tv_sec = start.tv_sec + expirationMillis / 1000;
+    uint64_t nsec = start.tv_nsec + (expirationMillis % 1000) * 1000000;
+    if (nsec < 1000000000) {
+        timeout.tv_nsec = nsec;
+    } else {
+        timeout.tv_sec += 1;
+        timeout.tv_nsec = nsec - 1000000000;
+    }
+
+    pthread_mutex_lock(&lock);
+    condResult = pthread_cond_timedwait(&cond, &lock, &timeout);
+    pthread_mutex_unlock(&lock);
+
+    if (condResult != 0) {
+        if (condResult == ETIMEDOUT) {
+            LOG(WARNING) << __func__ << " latency for AoC timeout: " << expirationMillis << " ms";
+        } else {
+            LOG(ERROR) << "Failed to wait for the condition variable: " << condResult;
+        }
+        ret = false;
+    } else {
+        for (const auto &residency : stateResidencies) {
+            residencies->emplace(residency.first, residency.second);
+        }
+    }
+
     return ret;
 }
 
diff --git a/powerstats/include/AocStateResidencyDataProvider.h b/powerstats/include/AocStateResidencyDataProvider.h
index 5008912..150f335 100644
--- a/powerstats/include/AocStateResidencyDataProvider.h
+++ b/powerstats/include/AocStateResidencyDataProvider.h
@@ -27,7 +27,8 @@ namespace stats {
 class AocStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
   public:
     AocStateResidencyDataProvider(std::vector<std::pair<std::string, std::string>> ids,
-                                  std::vector<std::pair<std::string, std::string>> states);
+                                  std::vector<std::pair<std::string, std::string>> states,
+                                  const uint64_t timeoutMillis);
     ~AocStateResidencyDataProvider() = default;
     bool getStateResidencies(
         std::unordered_map<std::string, std::vector<StateResidency>> *residencies) override;
@@ -36,10 +37,12 @@ class AocStateResidencyDataProvider : public PowerStats::IStateResidencyDataProv
   private:
     std::unordered_map<std::string /* entity name */,
         std::vector<std::unique_ptr<GenericStateResidencyDataProvider>> /* providers */> mProviders;
+    int32_t mStateCount;
+    uint64_t mTimeoutMillis;
 };
 
 }  // namespace stats
 }  // namespace power
 }  // namespace hardware
 }  // namespace android
-}  // namespace aidl
\ No newline at end of file
+}  // namespace aidl