• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /** @} */