1 /*
2  * Copyright (C) 2016 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 <condition_variable>
20 #include <chrono>
21 #include <functional>
22 #include <mutex>
23 #include <thread>
24 
25 #include <hidl/Status.h>
26 
27 namespace android {
28 namespace lshal {
29 
30 static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
31 
32 class BackgroundTaskState {
33 public:
BackgroundTaskState(std::function<void (void)> && func)34     explicit BackgroundTaskState(std::function<void(void)> &&func)
35             : mFunc(std::forward<decltype(func)>(func)) {}
notify()36     void notify() {
37         std::unique_lock<std::mutex> lock(mMutex);
38         mFinished = true;
39         lock.unlock();
40         mCondVar.notify_all();
41     }
42     template<class C, class D>
wait(std::chrono::time_point<C,D> end)43     bool wait(std::chrono::time_point<C, D> end) {
44         std::unique_lock<std::mutex> lock(mMutex);
45         mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
46         return mFinished;
47     }
operator()48     void operator()() {
49         mFunc();
50     }
51 private:
52     std::mutex mMutex;
53     std::condition_variable mCondVar;
54     bool mFinished = false;
55     std::function<void(void)> mFunc;
56 };
57 
callAndNotify(void * data)58 void *callAndNotify(void *data) {
59     BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
60     state();
61     state.notify();
62     return nullptr;
63 }
64 
65 template<class R, class P>
timeout(std::chrono::duration<R,P> delay,std::function<void (void)> && func)66 bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
67     auto now = std::chrono::system_clock::now();
68     BackgroundTaskState state{std::forward<decltype(func)>(func)};
69     pthread_t thread;
70     if (pthread_create(&thread, nullptr, callAndNotify, &state)) {
71         std::cerr << "FATAL: could not create background thread." << std::endl;
72         return false;
73     }
74     bool success = state.wait(now + delay);
75     if (!success) {
76         pthread_kill(thread, SIGINT);
77     }
78     pthread_join(thread, nullptr);
79     return success;
80 }
81 
82 template<class R, class P, class Function, class I, class... Args>
83 typename std::result_of<Function(I *, Args...)>::type
timeoutIPC(std::chrono::duration<R,P> wait,const sp<I> & interfaceObject,Function && func,Args &&...args)84 timeoutIPC(std::chrono::duration<R, P> wait, const sp<I> &interfaceObject, Function &&func,
85            Args &&... args) {
86     using ::android::hardware::Status;
87     typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
88     auto boundFunc = std::bind(std::forward<Function>(func),
89             interfaceObject.get(), std::forward<Args>(args)...);
90     bool success = timeout(wait, [&ret, &boundFunc] {
91         ret = std::move(boundFunc());
92     });
93     if (!success) {
94         return Status::fromStatusT(TIMED_OUT);
95     }
96     return ret;
97 }
98 
99 template<class Function, class I, class... Args>
100 typename std::result_of<Function(I *, Args...)>::type
timeoutIPC(const sp<I> & interfaceObject,Function && func,Args &&...args)101 timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
102     return timeoutIPC(IPC_CALL_WAIT, interfaceObject, func, args...);
103 }
104 
105 
106 }  // namespace lshal
107 }  // namespace android
108