From e2dadf0c4f132d3a39309f4e274f1a35f7caaaed Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Tue, 14 Feb 2017 15:43:31 -0800 Subject: [PATCH] lshal: Add timeout for IPC calls. 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 | 12 ++++---- cmds/lshal/Timeout.h | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 cmds/lshal/Timeout.h diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index d213fd314f..a2dabce90f 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -29,6 +29,8 @@ #include #include +#include "Timeout.h" + using ::android::hardware::hidl_string; using ::android::hidl::manager::V1_0::IServiceManager; @@ -163,7 +165,7 @@ Status Lshal::fetchAllLibraries(const sp &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 &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 &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 allDebugInfos; std::map> allPids; @@ -222,7 +224,7 @@ Status Lshal::fetchBinderized(const sp &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 &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(debugInfo.pid)].clear(); diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h new file mode 100644 index 0000000000..bf883c0a4c --- /dev/null +++ b/cmds/lshal/Timeout.h @@ -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 +#include +#include +#include +#include + +#include + +namespace android { +namespace lshal { + +static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500}; + +class BackgroundTaskState { +public: + BackgroundTaskState(){} + void notify() { + std::unique_lock lock(mMutex); + mFinished = true; + lock.unlock(); + mCondVar.notify_all(); + } + template + bool wait(std::chrono::time_point end) { + std::unique_lock 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 +bool timeout(std::chrono::duration delay, const std::function &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 +typename std::result_of::type +timeoutIPC(const sp &interfaceObject, Function &&func, Args &&... args) { + using ::android::hardware::Status; + typename std::result_of::type ret{Status::ok()}; + auto boundFunc = std::bind(std::forward(func), + interfaceObject.get(), std::forward(args)...); + bool success = timeout(IPC_CALL_WAIT, [&ret, &boundFunc] { + ret = boundFunc(); + }); + if (!success) { + return Status::fromStatusT(TIMED_OUT); + } + return ret; +} + +} // namespace lshal +} // namespace android -- 2.11.0