1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef HOS_TIMEOUTEXECUTOR_H
17 #define HOS_TIMEOUTEXECUTOR_H
18 #include <chrono>
19 #include <memory>
20 #include <thread>             // std::thread
21 #include <mutex>              // std::mutex, std::unique_lock
22 #include <condition_variable> // std::condition_variable
23 #include <type_traits>
24 #include <atomic>
25 #include <functional>
26 
27 namespace OHOS::TIMEOUTEXECUTOR {
28 
29 template<class... Params>
30 struct check_all_parameters;
31 
32 template <>
33 struct check_all_parameters<> {
34     static const bool value = true;
35 };
36 
37 template <class T, class... Others>
38 struct check_all_parameters<T, Others...> :
39     private check_all_parameters<Others...> {
40     using Mybase = check_all_parameters<Others...>;
41     static const bool value = \
42         std::is_scalar<T>::value && !std::is_pointer<T>::value && Mybase::value;
43 };
44 
45 template <class... Params>
46 using TypesCheck = check_all_parameters<Params...>;
47 
48 template <class FunctionType>
49 class BaseExecutor {
50     template<class... Params>
51     using TC = TypesCheck<Params...>;
52 public:
53     template<class _Fx, class Params = std::enable_if_t<true>>
54     struct FunctionImpl;
55 
56     // function requires parameters pass by value, no reference, no pointer
57     template<class Ret, class... Args>
58     struct FunctionImpl<Ret(Args...), std::enable_if_t<TC<Ret, Args...>::value>> {
59         using ResultType = Ret;
60         using CALLABLE = Ret(Args...);
61 
62         CALLABLE *ptr;
63         FunctionImpl(CALLABLE& callable) : ptr(&callable) {}
64 
65         ResultType invoke(Args... args)
66         {
67             return ((CALLABLE *)ptr)(args...);
68         }
69     };
70 
71     // only for class inherit from std::enable_shared_from_this<T>
72     template<class Ret, class Ct, class... Args>
73     struct FunctionImpl<Ret(Ct::*)(Args...), std::enable_if_t<TC<Ret, Args...>::value>> {
74         using ResultType = Ret;
75         using CALLABLE = Ret(Ct::*)(Args...);
76         using SHAREPOINTER = std::shared_ptr<Ct>;
77         using Derived = std::enable_if_t<std::is_base_of<std::enable_shared_from_this<Ct>, Ct>::value>;
78 
79         CALLABLE ptr;
80         FunctionImpl(CALLABLE& callable) : ptr(callable) {}
81 
82         ResultType invoke(SHAREPOINTER cls, Args... args)
83         {
84             return (cls.get()->*ptr)(args...);
85         }
86     };
87 
88     typedef FunctionImpl<FunctionType> Function;
89     typedef std::shared_ptr<Function> FunctionPointer;
90 
91 protected:
92     FunctionPointer _fx;
93 
94 public:
95     BaseExecutor(FunctionType& fx) : _fx(new Function(fx)) {}
96 };
97 
98 template <class FunctionType>
99 class TimeOutExecutor : public BaseExecutor<FunctionType> {
100     uint32_t _timeout = 2000; //milliseconds
101     using Mybase = BaseExecutor<FunctionType>;
102     using Result = typename BaseExecutor<FunctionType>::Function::ResultType;
103 
104 public:
105     enum ExecuteResult {
106         SUCCESS,
107         TIMEOUT
108     };
109 
110     TimeOutExecutor(FunctionType&& ft) : Mybase(ft) {}
111 
112     template<class... Args>
113     ExecuteResult Execute(Result &result, Args... args)
114     {
115         // add ref count
116         auto funcImpl = this->_fx;
117 
118         std::shared_ptr<std::condition_variable> cv =
119             std::make_shared<std::condition_variable>();
120 
121         std::shared_ptr<bool> is_detach =
122             std::make_shared<bool>(false);
123 
124         std::shared_ptr <std::mutex> mtxPointer = std::make_shared<std::mutex>();
125         std::unique_lock<std::mutex> lck(*mtxPointer);
126 
127         std::thread workThread([=, &result]() {
128             {
129                 std::unique_lock<std::mutex> lck(*mtxPointer);
130             }
131 
132 #ifndef NOLOG
133             std::chrono::system_clock::time_point begin = std::chrono::system_clock::now();
134 #endif
135             Result r = funcImpl->invoke(args...);
136 #ifndef NOLOG
137             std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
138             std::cout << "Actually execute time: " << \
139                 std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << " ms" << std::endl;
140 #endif
141             {
142                 std::unique_lock<std::mutex> lck(*mtxPointer);
143 
144                 if (false == (*is_detach)) {
145                     result = r;
146                     cv->notify_one(); //notify join thread completion
147                 }
148             }
149         });
150 
151         if (cv->wait_for(lck, std::chrono::milliseconds(_timeout)) ==
152             std::cv_status::timeout) {
153             *is_detach = true;
154             workThread.detach();    //detach work thread and return timeout
155             return TimeOutExecutor::TIMEOUT;
156         }
157 
158         workThread.join();
159         return TimeOutExecutor::SUCCESS;
160     }
161 
162     void SetTimeOut(uint32_t ms)
163     {
164         _timeout = ms;
165     }
166 
167     uint32_t GetTimeOut() const
168     {
169         return _timeout;
170     }
171 };
172 }
173 
174 #endif //HOS_TIMEOUTEXECUTOR_H