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#include <condition_variable>
18#include <chrono>
19#include <functional>
20#include <mutex>
21#include <thread>
22
23#include <hidl/Status.h>
24
25namespace android {
26namespace lshal {
27
28static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
29
30class BackgroundTaskState {
31public:
32    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    }
49private:
50    std::mutex mMutex;
51    std::condition_variable mCondVar;
52    bool mFinished = false;
53    std::function<void(void)> mFunc;
54};
55
56void *callAndNotify(void *data) {
57    BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
58    state();
59    state.notify();
60    return NULL;
61}
62
63template<class R, class P>
64bool 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, NULL, 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, NULL);
77    return success;
78}
79
80template<class Function, class I, class... Args>
81typename std::result_of<Function(I *, Args...)>::type
82timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
83    using ::android::hardware::Status;
84    typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
85    auto boundFunc = std::bind(std::forward<Function>(func),
86            interfaceObject.get(), std::forward<Args>(args)...);
87    bool success = timeout(IPC_CALL_WAIT, [&ret, &boundFunc] {
88        ret = std::move(boundFunc());
89    });
90    if (!success) {
91        return Status::fromStatusT(TIMED_OUT);
92    }
93    return ret;
94}
95
96}  // namespace lshal
97}  // namespace android
98