1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 use std::cmp;
15 use std::convert::TryInto;
16 use std::future::Future;
17 use std::pin::Pin;
18 use std::ptr::NonNull;
19 use std::task::{Context, Poll};
20 use std::time::{Duration, Instant};
21
22 cfg_not_ffrt!(
23 use std::sync::Arc;
24 use crate::executor::driver::Handle;
25 );
26
27 use crate::time::Clock;
28 #[cfg(feature = "ffrt")]
29 use crate::time::TimeDriver;
30
31 const TEN_YEARS: Duration = Duration::from_secs(86400 * 365 * 10);
32
33 /// Waits until 'instant' has reached.
34 ///
35 /// # Panic
36 /// Calling this method outside of a Ylong Runtime could cause panic, for
37 /// example, outside of an async closure that is passed to ylong_runtime::spawn
38 /// or ylong_runtime::block_on. The async wrapping is necessary since it makes
39 /// the function become lazy in order to get successfully executed on the
40 /// runtime.
sleep_until(instant: Instant) -> Sleep41 pub fn sleep_until(instant: Instant) -> Sleep {
42 Sleep::new_timeout(instant)
43 }
44
45 /// Waits until 'duration' has elapsed.
46 ///
47 /// # Panic
48 /// Calling this method outside of a Ylong Runtime could cause panic, for
49 /// example, outside of an async closure that is passed to ylong_runtime::spawn
50 /// or ylong_runtime::block_on. The async wrapping is necessary since it makes
51 /// the function become lazy in order to get successfully executed on the
52 /// runtime.
sleep(duration: Duration) -> Sleep53 pub fn sleep(duration: Duration) -> Sleep {
54 // If the time reaches the maximum value,
55 // then set the default timing time to 10 years.
56 match Instant::now().checked_add(duration) {
57 Some(deadline) => Sleep::new_timeout(deadline),
58 None => Sleep::new_timeout(Instant::now() + TEN_YEARS),
59 }
60 }
61
62 /// [`Sleep`](Sleep) is a structure that implements Future.
63 ///
64 /// [`Sleep`](Sleep) will be returned by func ['sleep'](sleep).
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use std::time::Duration;
70 ///
71 /// use ylong_runtime::time::sleep;
72 ///
73 /// async fn sleep_test() {
74 /// let sleep = sleep(Duration::from_secs(2)).await;
75 /// println!("2 secs have elapsed");
76 /// }
77 /// ```
78 pub struct Sleep {
79 // During the polling of this structure, no repeated insertion.
80 need_insert: bool,
81
82 // The time at which the structure should end.
83 deadline: Instant,
84
85 // Corresponding Timer structure.
86 timer: Clock,
87
88 #[cfg(not(feature = "ffrt"))]
89 handle: Arc<Handle>,
90 }
91
92 impl Sleep {
93 // Creates a Sleep structure based on the given deadline.
new_timeout(deadline: Instant) -> Self94 fn new_timeout(deadline: Instant) -> Self {
95 #[cfg(not(feature = "ffrt"))]
96 let handle = Handle::get_handle().expect("sleep new out of worker ctx");
97
98 #[cfg(feature = "ffrt")]
99 let handle = TimeDriver::get_ref();
100
101 let start_time = handle.start_time();
102 let deadline = cmp::max(deadline, start_time);
103
104 let timer = Clock::new();
105 Self {
106 need_insert: true,
107 deadline,
108 timer,
109 #[cfg(not(feature = "ffrt"))]
110 handle,
111 }
112 }
113
114 // Returns the deadline of the Sleep
deadline(&self) -> Instant115 pub(crate) fn deadline(&self) -> Instant {
116 self.deadline
117 }
118
119 // Resets the deadline of the Sleep
reset(&mut self, new_deadline: Instant)120 pub(crate) fn reset(&mut self, new_deadline: Instant) {
121 self.need_insert = true;
122 self.deadline = new_deadline;
123 self.timer.set_result(false);
124 }
125
126 // Cancels the Sleep
cancel(&mut self)127 fn cancel(&mut self) {
128 #[cfg(not(feature = "ffrt"))]
129 let driver = &self.handle;
130 #[cfg(feature = "ffrt")]
131 let driver = TimeDriver::get_ref();
132 driver.timer_cancel(NonNull::from(&self.timer));
133 }
134 }
135
136 impl Future for Sleep {
137 type Output = ();
138
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>139 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
140 let this = self.get_mut();
141
142 #[cfg(not(feature = "ffrt"))]
143 let driver = &this.handle;
144 #[cfg(feature = "ffrt")]
145 let driver = TimeDriver::get_ref();
146 if this.need_insert {
147 let ms = this
148 .deadline
149 .checked_duration_since(driver.start_time())
150 .unwrap()
151 .as_millis()
152 .try_into()
153 .unwrap_or(u64::MAX);
154 this.timer.set_expiration(ms);
155 this.timer.set_waker(cx.waker().clone());
156
157 match driver.timer_register(NonNull::from(&this.timer)) {
158 Ok(_) => this.need_insert = false,
159 Err(_) => {
160 // Even if the insertion fails, there is no need to insert again here,
161 // it is a timeout clock and needs to be triggered immediately at the next poll.
162 this.need_insert = false;
163 this.timer.set_result(true);
164 }
165 }
166 }
167
168 if this.timer.result() {
169 Poll::Ready(())
170 } else {
171 Poll::Pending
172 }
173 }
174 }
175
176 impl Drop for Sleep {
drop(&mut self)177 fn drop(&mut self) {
178 // For some uses, for example, Timeout,
179 // `Sleep` enters the `Pending` state first and inserts the `TimerHandle` into
180 // the `DRIVER`, the future of timeout returns `Ready` in advance of the
181 // next polling, as a result, the `TimerHandle` pointer in the `DRIVER`
182 // is invalid. need to cancel the `TimerHandle` operation during `Sleep`
183 // drop.
184 self.cancel()
185 }
186 }
187
188 #[cfg(test)]
189 mod test {
190 use std::time::Duration;
191
192 use crate::time::sleep;
193 use crate::{block_on, spawn};
194
195 /// UT test cases for new_sleep
196 ///
197 /// # Brief
198 /// 1. Uses sleep to create a Sleep Struct.
199 /// 2. Uses block_on to test different sleep duration.
200 #[test]
new_timer_sleep()201 fn new_timer_sleep() {
202 block_on(async move {
203 sleep(Duration::new(0, 20_000_000)).await;
204 sleep(Duration::new(0, 20_000_000)).await;
205 sleep(Duration::new(0, 20_000_000)).await;
206 });
207
208 let handle_one = spawn(async {
209 sleep(Duration::new(0, 20_000_000)).await;
210 });
211 let handle_two = spawn(async {
212 sleep(Duration::new(0, 20_000_000)).await;
213 });
214 let handle_three = spawn(async {
215 sleep(Duration::new(0, 20_000_000)).await;
216 });
217 block_on(handle_one).unwrap();
218 block_on(handle_two).unwrap();
219 block_on(handle_three).unwrap();
220 }
221 }
222