OSDN Git Service

lshal: Add timeout for IPC calls.
authorYifan Hong <elsk@google.com>
Tue, 14 Feb 2017 23:43:31 +0000 (15:43 -0800)
committerYifan Hong <elsk@google.com>
Wed, 15 Feb 2017 23:51:12 +0000 (15:51 -0800)
IPC calls into interfaces should be done fault-torelently.
Add a timeout for each IPC call made so that lshal won't be
indefinitely blocked even if the interface don't reply
promptly.

Bug: 35317039

Test: lshal

Change-Id: Icb8157716ad68bddb5b33304b9063aa6f233985d

cmds/lshal/Lshal.cpp
cmds/lshal/Timeout.h [new file with mode: 0644]

index d213fd3..a2dabce 100644 (file)
@@ -29,6 +29,8 @@
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 
+#include "Timeout.h"
+
 using ::android::hardware::hidl_string;
 using ::android::hidl::manager::V1_0::IServiceManager;
 
@@ -163,7 +165,7 @@ Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
     using namespace ::android::hardware;
     using namespace ::android::hidl::manager::V1_0;
     using namespace ::android::hidl::base::V1_0;
-    auto ret = manager->list([&] (const auto &fqInstanceNames) {
+    auto ret = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
         for (const auto &fqInstanceName : fqInstanceNames) {
             putEntry({
                 .interfaceName = fqInstanceName,
@@ -186,7 +188,7 @@ Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
     using namespace ::android::hardware;
     using namespace ::android::hidl::manager::V1_0;
     using namespace ::android::hidl::base::V1_0;
-    auto ret = manager->debugDump([&] (const auto &infos) {
+    auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
         for (const auto &info : infos) {
             putEntry({
                 .interfaceName =
@@ -214,7 +216,7 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
     using namespace ::android::hidl::base::V1_0;
     const std::string mode = "hwbinder";
     Status status = OK;
-    auto listRet = manager->list([&] (const auto &fqInstanceNames) {
+    auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
         // server pid, .ptr value of binder object, child pids
         std::map<std::string, DebugInfo> allDebugInfos;
         std::map<pid_t, std::map<uint64_t, Pids>> allPids;
@@ -222,7 +224,7 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
             const auto pair = split(fqInstanceName, '/');
             const auto &serviceName = pair.first;
             const auto &instanceName = pair.second;
-            auto getRet = manager->get(serviceName, instanceName);
+            auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
             if (!getRet.isOk()) {
                 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
                      << "cannot be fetched from service manager:"
@@ -237,7 +239,7 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
                 status |= DUMP_BINDERIZED_ERROR;
                 continue;
             }
-            auto debugRet = service->getDebugInfo([&] (const auto &debugInfo) {
+            auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
                 allDebugInfos[fqInstanceName] = debugInfo;
                 if (debugInfo.pid >= 0) {
                     allPids[static_cast<pid_t>(debugInfo.pid)].clear();
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
new file mode 100644 (file)
index 0000000..bf883c0
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <condition_variable>
+#include <chrono>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <hidl/Status.h>
+
+namespace android {
+namespace lshal {
+
+static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
+
+class BackgroundTaskState {
+public:
+    BackgroundTaskState(){}
+    void notify() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mFinished = true;
+        lock.unlock();
+        mCondVar.notify_all();
+    }
+    template<class C, class D>
+    bool wait(std::chrono::time_point<C, D> end) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
+        return mFinished;
+    }
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondVar;
+    bool mFinished = false;
+};
+
+template<class R, class P>
+bool timeout(std::chrono::duration<R, P> delay, const std::function<void(void)> &func) {
+    auto now = std::chrono::system_clock::now();
+    BackgroundTaskState state{};
+    std::thread t([&state, &func] {
+        func();
+        state.notify();
+    });
+    t.detach();
+    bool success = state.wait(now + delay);
+    return success;
+}
+
+template<class Function, class I, class... Args>
+typename std::result_of<Function(I *, Args...)>::type
+timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
+    using ::android::hardware::Status;
+    typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
+    auto boundFunc = std::bind(std::forward<Function>(func),
+            interfaceObject.get(), std::forward<Args>(args)...);
+    bool success = timeout(IPC_CALL_WAIT, [&ret, &boundFunc] {
+        ret = boundFunc();
+    });
+    if (!success) {
+        return Status::fromStatusT(TIMED_OUT);
+    }
+    return ret;
+}
+
+}  // namespace lshal
+}  // namespace android