1 /* 2 * Copyright (c) 2023 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 /** 17 * @addtogroup FFRT 18 * @{ 19 * 20 * @brief Provides FFRT C++ APIs. 21 * 22 * @since 10 23 */ 24 25 /** 26 * @file condition_variable.h 27 * 28 * @brief Declares the condition variable interfaces in C++. 29 * 30 * @library libffrt.z.so 31 * @kit FunctionFlowRuntimeKit 32 * @syscap SystemCapability.Resourceschedule.Ffrt.Core 33 * @since 10 34 */ 35 36 #ifndef FFRT_API_CPP_CONDITION_VARIABLE_H 37 #define FFRT_API_CPP_CONDITION_VARIABLE_H 38 39 #include <chrono> 40 #include <mutex> 41 #include "mutex.h" 42 #include "c/condition_variable.h" 43 44 namespace ffrt { 45 /** 46 * @enum cv_status 47 * @brief Specifies the result of a condition variable wait operation. 48 * 49 * @since 10 50 */ 51 enum class cv_status { 52 no_timeout, /**< Indicates that the wait ended because the condition was met. */ 53 timeout /**< Indicates that the wait ended due to a timeout. */ 54 }; 55 56 /** 57 * @class condition_variable 58 * @brief A class implementing condition variable synchronization primitives. 59 * 60 * Provides mechanisms for threads to wait for a notification or a specified condition to be met, 61 * supporting both timed and non-timed operations. 62 * 63 * @since 10 64 */ 65 class condition_variable : public ffrt_cond_t { 66 public: 67 /** 68 * @brief Constructs a new condition_variable object. 69 * 70 * @since 10 71 */ condition_variable()72 condition_variable() 73 { 74 ffrt_cond_init(this, nullptr); 75 } 76 77 /** 78 * @brief Destroys the condition_variable object. 79 * 80 * @since 10 81 */ ~condition_variable()82 ~condition_variable() noexcept 83 { 84 ffrt_cond_destroy(this); 85 } 86 87 /** 88 * @brief Deleted copy constructor to prevent copying of the condition_variable object. 89 */ 90 condition_variable(const condition_variable&) = delete; 91 92 /** 93 * @brief Deleted copy assignment operator to prevent assignment of the condition_variable object. 94 */ 95 condition_variable& operator=(const condition_variable&) = delete; 96 97 /** 98 * @brief Waits until a predicate is satisfied or a timeout occurs. 99 * 100 * @tparam Clock The clock type used for the timeout. 101 * @tparam Duration The duration type used for the timeout. 102 * @tparam Pred The predicate type. 103 * @param lk A unique lock on the associated mutex. 104 * @param tp The time point when the wait should time out. 105 * @param pred The predicate to be satisfied. 106 * @return true if the predicate was satisfied before the timeout, false otherwise. 107 * @since 10 108 */ 109 template <typename Clock, typename Duration, typename Pred> wait_until(std::unique_lock<mutex> & lk,const std::chrono::time_point<Clock,Duration> & tp,Pred && pred)110 bool wait_until( 111 std::unique_lock<mutex>& lk, const std::chrono::time_point<Clock, Duration>& tp, Pred&& pred) noexcept 112 { 113 while (!pred()) { 114 if (wait_until(lk, tp) == cv_status::timeout) { 115 return pred(); 116 } 117 } 118 return true; 119 } 120 121 /** 122 * @brief Waits until a specified time point. 123 * 124 * @tparam Clock The clock type used for the timeout. 125 * @tparam Duration The duration type used for the timeout. 126 * @param lk A unique lock on the associated mutex. 127 * @param tp The time point when the wait should time out. 128 * @return A cv_status value indicating whether the wait ended due to a timeout or a condition. 129 * @since 10 130 */ 131 template <typename Clock, typename Duration> wait_until(std::unique_lock<mutex> & lk,const std::chrono::time_point<Clock,Duration> & tp)132 cv_status wait_until(std::unique_lock<mutex>& lk, const std::chrono::time_point<Clock, Duration>& tp) noexcept 133 { 134 return _wait_for(lk, tp - Clock::now()); 135 } 136 137 /** 138 * @brief Waits for a specified duration. 139 * 140 * @tparam Rep The representation type of the duration. 141 * @tparam Period The period type of the duration. 142 * @param lk A unique lock on the associated mutex. 143 * @param sleep_time The duration to wait for. 144 * @return A cv_status value indicating whether the wait ended due to a timeout or a condition. 145 * @since 10 146 */ 147 template <typename Rep, typename Period> wait_for(std::unique_lock<mutex> & lk,const std::chrono::duration<Rep,Period> & sleep_time)148 cv_status wait_for(std::unique_lock<mutex>& lk, const std::chrono::duration<Rep, Period>& sleep_time) noexcept 149 { 150 return _wait_for(lk, sleep_time); 151 } 152 153 /** 154 * @brief Waits for a specified duration or until a predicate is satisfied. 155 * 156 * @tparam Rep The representation type of the duration. 157 * @tparam Period The period type of the duration. 158 * @tparam Pred The predicate type. 159 * @param lk A unique lock on the associated mutex. 160 * @param sleepTime The duration to wait for. 161 * @param pred The predicate to be satisfied. 162 * @return true if the predicate was satisfied before the timeout, false otherwise. 163 * @since 10 164 */ 165 template <typename Rep, typename Period, typename Pred> wait_for(std::unique_lock<mutex> & lk,const std::chrono::duration<Rep,Period> & sleepTime,Pred && pred)166 bool wait_for( 167 std::unique_lock<mutex>& lk, const std::chrono::duration<Rep, Period>& sleepTime, Pred&& pred) noexcept 168 { 169 if (sleepTime <= sleepTime.zero()) { 170 return pred(); 171 } 172 173 auto now = std::chrono::steady_clock::now(); 174 auto nowNs = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()); 175 auto sleepNs = std::chrono::duration_cast<std::chrono::nanoseconds>(sleepTime); 176 std::chrono::steady_clock::time_point absoluteTime; 177 if (sleepNs.count() > ((std::chrono::nanoseconds::max)().count() - nowNs.count())) { 178 absoluteTime = (std::chrono::steady_clock::time_point::max)(); 179 } else { 180 absoluteTime = now + sleepNs; 181 } 182 183 return wait_until(lk, absoluteTime, std::forward<Pred>(pred)); 184 } 185 186 /** 187 * @brief Waits until a predicate is satisfied. 188 * 189 * @tparam Pred The predicate type. 190 * @param lk A unique lock on the associated mutex. 191 * @param pred The predicate to be satisfied. 192 * @since 10 193 */ 194 template <typename Pred> wait(std::unique_lock<mutex> & lk,Pred && pred)195 void wait(std::unique_lock<mutex>& lk, Pred&& pred) 196 { 197 while (!pred()) { 198 wait(lk); 199 } 200 } 201 202 /** 203 * @brief Waits indefinitely until notified. 204 * 205 * @param lk A unique lock on the associated mutex. 206 * @since 10 207 */ wait(std::unique_lock<mutex> & lk)208 void wait(std::unique_lock<mutex>& lk) 209 { 210 ffrt_cond_wait(this, lk.mutex()); 211 } 212 213 /** 214 * @brief Notifies one thread waiting on the condition variable. 215 * 216 * @since 10 217 */ notify_one()218 void notify_one() noexcept 219 { 220 ffrt_cond_signal(this); 221 } 222 223 /** 224 * @brief Notifies all threads waiting on the condition variable. 225 * 226 * @since 10 227 */ notify_all()228 void notify_all() noexcept 229 { 230 ffrt_cond_broadcast(this); 231 } 232 233 private: 234 /** 235 * @brief Internal function to perform a timed wait. 236 * 237 * @tparam Rep The representation type of the duration. 238 * @tparam Period The period type of the duration. 239 * @param lk A unique lock on the associated mutex. 240 * @param dur The duration to wait for. 241 * @return A cv_status value indicating whether the wait ended due to a timeout or a condition. 242 * @since 10 243 */ 244 template <typename Rep, typename Period> _wait_for(std::unique_lock<mutex> & lk,const std::chrono::duration<Rep,Period> & dur)245 cv_status _wait_for(std::unique_lock<mutex>& lk, const std::chrono::duration<Rep, Period>& dur) noexcept 246 { 247 if (dur <= dur.zero()) { 248 return cv_status::timeout; 249 } 250 251 auto now_ns = std::chrono::duration_cast<std::chrono::nanoseconds>( 252 std::chrono::steady_clock::now().time_since_epoch()); 253 auto dur_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(dur); 254 255 std::chrono::nanoseconds ns; 256 if (now_ns.count() > (std::chrono::nanoseconds::max)().count() - dur_ns.count()) { 257 ns = (std::chrono::nanoseconds::max)(); 258 } else { 259 ns = now_ns + dur_ns; 260 } 261 262 timespec ts; 263 ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(ns).count(); 264 ns -= std::chrono::seconds(ts.tv_sec); 265 ts.tv_nsec = static_cast<long>(ns.count()); 266 267 auto ret = ffrt_cond_timedwait(this, lk.mutex(), &ts); 268 if (ret == ffrt_success) { 269 return cv_status::no_timeout; 270 } 271 return cv_status::timeout; 272 } 273 }; 274 } // namespace ffrt 275 276 #endif // FFRT_API_CPP_CONDITION_VARIABLE_H 277 /** @} */