Skip to content

Commit 1513627

Browse files
committed
Revert^2 "lshal: use std::async"
This reverts commit 2cfbc51. Reason for revert: reapply change Bug: 323268003 Bug: 311143089 Test: See next CL Change-Id: I392eca8f3368c1d74b4de37d5f49663d3ddbf7e0
1 parent 6b13e50 commit 1513627

3 files changed

Lines changed: 143 additions & 73 deletions

File tree

cmds/lshal/Timeout.h

Lines changed: 26 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,83 +16,44 @@
1616

1717
#pragma once
1818

19-
#include <condition_variable>
2019
#include <chrono>
21-
#include <functional>
22-
#include <mutex>
23-
#include <thread>
20+
#include <future>
2421

2522
#include <hidl/Status.h>
23+
#include <utils/Errors.h>
2624

2725
namespace android {
2826
namespace lshal {
2927

30-
class BackgroundTaskState {
31-
public:
32-
explicit BackgroundTaskState(std::function<void(void)> &&func)
33-
: mFunc(std::forward<decltype(func)>(func)) {}
34-
void notify() {
35-
std::unique_lock<std::mutex> lock(mMutex);
36-
mFinished = true;
37-
lock.unlock();
38-
mCondVar.notify_all();
39-
}
40-
template<class C, class D>
41-
bool wait(std::chrono::time_point<C, D> end) {
42-
std::unique_lock<std::mutex> lock(mMutex);
43-
mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
44-
return mFinished;
45-
}
46-
void operator()() {
47-
mFunc();
48-
}
49-
private:
50-
std::mutex mMutex;
51-
std::condition_variable mCondVar;
52-
bool mFinished = false;
53-
std::function<void(void)> mFunc;
54-
};
55-
56-
void *callAndNotify(void *data) {
57-
BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
58-
state();
59-
state.notify();
60-
return nullptr;
61-
}
62-
63-
template<class R, class P>
64-
bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
65-
auto now = std::chrono::system_clock::now();
66-
BackgroundTaskState state{std::forward<decltype(func)>(func)};
67-
pthread_t thread;
68-
if (pthread_create(&thread, nullptr, callAndNotify, &state)) {
69-
std::cerr << "FATAL: could not create background thread." << std::endl;
70-
return false;
71-
}
72-
bool success = state.wait(now + delay);
73-
if (!success) {
74-
pthread_kill(thread, SIGINT);
75-
}
76-
pthread_join(thread, nullptr);
77-
return success;
78-
}
79-
28+
// Call function on interfaceObject and wait for result until the given timeout has reached.
29+
// Callback functions pass to timeoutIPC() may be executed after the this function
30+
// has returned, especially if deadline has been reached. Hence, care must be taken when passing
31+
// data between the background thread and the main thread. See b/311143089.
8032
template<class R, class P, class Function, class I, class... Args>
8133
typename std::invoke_result<Function, I *, Args...>::type
8234
timeoutIPC(std::chrono::duration<R, P> wait, const sp<I> &interfaceObject, Function &&func,
8335
Args &&... args) {
8436
using ::android::hardware::Status;
85-
typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
86-
auto boundFunc = std::bind(std::forward<Function>(func),
87-
interfaceObject.get(), std::forward<Args>(args)...);
88-
bool success = timeout(wait, [&ret, &boundFunc] {
89-
ret = std::move(boundFunc());
90-
});
91-
if (!success) {
37+
38+
// Execute on a background thread but do not defer execution.
39+
auto future =
40+
std::async(std::launch::async, func, interfaceObject, std::forward<Args>(args)...);
41+
auto status = future.wait_for(wait);
42+
if (status == std::future_status::ready) {
43+
return future.get();
44+
}
45+
46+
// This future belongs to a background thread that we no longer care about.
47+
// Putting this in the global list avoids std::future::~future() that may wait for the
48+
// result to come back.
49+
// This leaks memory, but lshal is a debugging tool, so this is fine.
50+
static std::vector<decltype(future)> gDeadPool{};
51+
gDeadPool.emplace_back(std::move(future));
52+
53+
if (status == std::future_status::timeout) {
9254
return Status::fromStatusT(TIMED_OUT);
9355
}
94-
return ret;
56+
return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE, "Illegal future_status");
9557
}
96-
97-
} // namespace lshal
98-
} // namespace android
58+
} // namespace lshal
59+
} // namespace android

cmds/lshal/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818

1919
int main(int argc, char **argv) {
2020
using namespace ::android::lshal;
21-
return Lshal{}.main(Arg{argc, argv});
21+
// Use _exit() to force terminate background threads in Timeout.h
22+
_exit(Lshal{}.main(Arg{argc, argv}));
2223
}

cmds/lshal/test.cpp

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
* limitations under the License.
1515
*/
1616

17+
#include <chrono>
18+
#include <future>
19+
#include <mutex>
20+
#include "android/hidl/base/1.0/IBase.h"
1721
#define LOG_TAG "Lshal"
1822
#include <android-base/logging.h>
1923

@@ -36,6 +40,8 @@
3640

3741
using namespace testing;
3842

43+
using std::chrono_literals::operator""ms;
44+
3945
using ::android::hidl::base::V1_0::DebugInfo;
4046
using ::android::hidl::base::V1_0::IBase;
4147
using ::android::hidl::manager::V1_0::IServiceManager;
@@ -934,12 +940,9 @@ TEST_F(ListTest, DumpDebug) {
934940
return hardware::Void();
935941
}));
936942
EXPECT_CALL(*serviceManager, get(_, _))
937-
.WillRepeatedly(
938-
Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
939-
int id = getIdFromInstanceName(instance);
940-
if (id > inheritanceLevel) return nullptr;
941-
return sp<IBase>(service);
942-
}));
943+
.WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
944+
return sp<IBase>(service);
945+
}));
943946

944947
const std::string expected = "[fake description 0]\n"
945948
"Interface\n"
@@ -957,6 +960,110 @@ TEST_F(ListTest, DumpDebug) {
957960
EXPECT_EQ("", err.str());
958961
}
959962

963+
// In SlowService, everything goes slooooooow. Each IPC call will wait for
964+
// the specified time before calling the callback function or returning.
965+
class SlowService : public IBase {
966+
public:
967+
explicit SlowService(std::chrono::milliseconds wait) : mWait(wait) {}
968+
android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
969+
std::this_thread::sleep_for(mWait);
970+
cb(getInterfaceName(1));
971+
storeHistory("interfaceDescriptor");
972+
return hardware::Void();
973+
}
974+
android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
975+
std::this_thread::sleep_for(mWait);
976+
std::vector<hidl_string> ret;
977+
ret.push_back(getInterfaceName(1));
978+
ret.push_back(IBase::descriptor);
979+
cb(ret);
980+
storeHistory("interfaceChain");
981+
return hardware::Void();
982+
}
983+
android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
984+
std::this_thread::sleep_for(mWait);
985+
std::vector<hidl_hash> ret;
986+
ret.push_back(getHashFromId(0));
987+
ret.push_back(getHashFromId(0xff));
988+
cb(ret);
989+
storeHistory("getHashChain");
990+
return hardware::Void();
991+
}
992+
android::hardware::Return<void> debug(const hidl_handle&,
993+
const hidl_vec<hidl_string>&) override {
994+
std::this_thread::sleep_for(mWait);
995+
storeHistory("debug");
996+
return Void();
997+
}
998+
999+
template <class R, class P, class Pred>
1000+
bool waitForHistory(std::chrono::duration<R, P> wait, Pred predicate) {
1001+
std::unique_lock<std::mutex> lock(mLock);
1002+
return mCv.wait_for(lock, wait, [&]() { return predicate(mCallHistory); });
1003+
}
1004+
1005+
private:
1006+
void storeHistory(std::string hist) {
1007+
{
1008+
std::lock_guard<std::mutex> lock(mLock);
1009+
mCallHistory.emplace_back(std::move(hist));
1010+
}
1011+
mCv.notify_all();
1012+
}
1013+
1014+
const std::chrono::milliseconds mWait;
1015+
std::mutex mLock;
1016+
std::condition_variable mCv;
1017+
// List of functions that have finished being called on this interface.
1018+
std::vector<std::string> mCallHistory;
1019+
};
1020+
1021+
class TimeoutTest : public ListTest {
1022+
public:
1023+
void setMockServiceManager(sp<IBase> service) {
1024+
EXPECT_CALL(*serviceManager, list(_))
1025+
.WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
1026+
std::vector<hidl_string> ret;
1027+
ret.push_back(getInterfaceName(1) + "/default");
1028+
cb(ret);
1029+
return hardware::Void();
1030+
}));
1031+
EXPECT_CALL(*serviceManager, get(_, _))
1032+
.WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
1033+
return service;
1034+
}));
1035+
}
1036+
};
1037+
1038+
TEST_F(TimeoutTest, BackgroundThreadIsKept) {
1039+
auto lshalIpcTimeout = 100ms;
1040+
auto serviceIpcTimeout = 200ms;
1041+
lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
1042+
sp<SlowService> service = new SlowService(serviceIpcTimeout);
1043+
setMockServiceManager(service);
1044+
1045+
optind = 1; // mimic Lshal::parseArg()
1046+
EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
1047+
EXPECT_THAT(err.str(), HasSubstr("Skipping \"a.h.foo1@1.0::IFoo/default\""));
1048+
EXPECT_TRUE(service->waitForHistory(serviceIpcTimeout * 5, [](const auto& hist) {
1049+
return hist.size() == 1 && hist[0] == "interfaceChain";
1050+
})) << "The background thread should continue after the main thread moves on, but it is killed";
1051+
}
1052+
1053+
TEST_F(TimeoutTest, BackgroundThreadDoesNotBlockMainThread) {
1054+
auto lshalIpcTimeout = 100ms;
1055+
auto serviceIpcTimeout = 2000ms;
1056+
auto start = std::chrono::system_clock::now();
1057+
lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
1058+
sp<SlowService> service = new SlowService(serviceIpcTimeout);
1059+
setMockServiceManager(service);
1060+
1061+
optind = 1; // mimic Lshal::parseArg()
1062+
EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
1063+
EXPECT_LE(std::chrono::system_clock::now(), start + 5 * lshalIpcTimeout)
1064+
<< "The main thread should not be blocked by the background task";
1065+
}
1066+
9601067
class ListVintfTest : public ListTest {
9611068
public:
9621069
virtual void SetUp() override {
@@ -1079,5 +1186,6 @@ TEST_F(HelpTest, UnknownOptionHelp2) {
10791186

10801187
int main(int argc, char **argv) {
10811188
::testing::InitGoogleMock(&argc, argv);
1082-
return RUN_ALL_TESTS();
1189+
// Use _exit() to force terminate background threads in Timeout.h
1190+
_exit(RUN_ALL_TESTS());
10831191
}

0 commit comments

Comments
 (0)