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