Skip to content

Commit e8c34fd

Browse files
committed
init futures module
1 parent a08e02d commit e8c34fd

File tree

2 files changed

+278
-0
lines changed

2 files changed

+278
-0
lines changed

glib/src/futures/mod.rs

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
use futures_task::FutureObj;
2+
use futures_task::LocalFutureObj;
3+
use futures_task::LocalSpawn;
4+
use futures_task::Poll;
5+
use futures_task::Spawn;
6+
use futures_util::future::Either;
7+
use futures_util::future::Select;
8+
use futures_util::pin_mut;
9+
10+
use crate::FutureWithTimeoutError;
11+
use crate::JoinHandle;
12+
use crate::MainContext;
13+
use crate::Priority;
14+
use crate::SpawnWithinJoinHandle;
15+
use std::future::{Future, IntoFuture};
16+
use std::{pin::Pin, time::Duration};
17+
18+
#[derive(Default, Debug, Eq, PartialEq)]
19+
pub enum SchedulingPrecision {
20+
#[default]
21+
Millisecond,
22+
Second,
23+
}
24+
25+
#[derive(Default, Debug, Eq, PartialEq)]
26+
pub struct Sleep {
27+
duration: Duration,
28+
priority: Priority,
29+
precision: SchedulingPrecision,
30+
}
31+
32+
impl IntoFuture for Sleep {
33+
type Output = ();
34+
35+
type IntoFuture = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
36+
37+
fn into_future(self) -> Self::IntoFuture {
38+
use SchedulingPrecision::*;
39+
match self.precision {
40+
Millisecond => crate::timeout_future_with_priority(self.priority, self.duration),
41+
Second => crate::timeout_future_seconds_with_priority(
42+
self.priority,
43+
self.duration.as_secs_f32().round() as u32,
44+
),
45+
}
46+
}
47+
}
48+
49+
impl Sleep {
50+
pub fn priority(mut self, priority: Priority) -> Self {
51+
self.priority = priority;
52+
self
53+
}
54+
pub fn precision(mut self, precision: SchedulingPrecision) -> Self {
55+
self.precision = precision;
56+
self
57+
}
58+
}
59+
60+
pub fn sleep(duration: Duration) -> Sleep {
61+
Sleep {
62+
priority: crate::PRIORITY_DEFAULT,
63+
duration,
64+
precision: SchedulingPrecision::Millisecond,
65+
}
66+
}
67+
68+
/// Options to build a future that will run until the specified `duration` passes.
69+
#[derive(Default, Debug, Eq, PartialEq)]
70+
pub struct Timeout<F: Future> {
71+
duration: Duration,
72+
priority: Priority,
73+
precision: SchedulingPrecision,
74+
future: F,
75+
}
76+
pub struct TimeoutFuture<F> {
77+
select: Select<F, Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
78+
}
79+
80+
impl<F: Future + Unpin> Future for TimeoutFuture<F> {
81+
type Output = Result<F::Output, FutureWithTimeoutError>;
82+
83+
fn poll(
84+
mut self: Pin<&mut Self>,
85+
cx: &mut futures_task::Context<'_>,
86+
) -> futures_task::Poll<Self::Output> {
87+
let select = &mut self.as_mut().select;
88+
pin_mut!(select);
89+
match select.poll(cx) {
90+
Poll::Ready(res) => match res {
91+
Either::Left(value) => Poll::Ready(Ok(value.0)),
92+
Either::Right(_timedout) => Poll::Ready(Err(FutureWithTimeoutError)),
93+
},
94+
Poll::Pending => Poll::Pending,
95+
}
96+
}
97+
}
98+
99+
impl<F: Future + std::marker::Unpin + 'static> IntoFuture for Timeout<F> {
100+
type Output = Result<F::Output, FutureWithTimeoutError>;
101+
102+
type IntoFuture = TimeoutFuture<F>;
103+
104+
fn into_future(self) -> Self::IntoFuture {
105+
let sleep = Sleep {
106+
duration: self.duration,
107+
precision: self.precision,
108+
priority: self.priority,
109+
};
110+
TimeoutFuture {
111+
select: futures_util::future::select(self.future, sleep.into_future()),
112+
}
113+
}
114+
}
115+
116+
impl<F: Future> Timeout<F> {
117+
pub fn priority(mut self, priority: Priority) -> Self {
118+
self.priority = priority;
119+
self
120+
}
121+
pub fn precision(mut self, precision: SchedulingPrecision) -> Self {
122+
self.precision = precision;
123+
self
124+
}
125+
}
126+
127+
pub fn timeout<F: Future>(
128+
duration: Duration,
129+
future: F,
130+
) -> Timeout<impl Future<Output = F::Output>> {
131+
Timeout {
132+
duration,
133+
priority: crate::PRIORITY_DEFAULT,
134+
future: Box::pin(future),
135+
precision: SchedulingPrecision::Millisecond,
136+
}
137+
}
138+
139+
#[derive(Default, Debug, Eq, PartialEq)]
140+
pub struct SpawnOptions {
141+
priority: Priority,
142+
context: Option<crate::MainContext>,
143+
}
144+
145+
impl SpawnOptions {
146+
pub fn new() -> Self {
147+
SpawnOptions {
148+
priority: crate::PRIORITY_DEFAULT,
149+
context: None,
150+
}
151+
}
152+
pub fn priority(&mut self, priority: Priority) -> &mut Self {
153+
self.priority = priority;
154+
self
155+
}
156+
pub fn context(&mut self, context: MainContext) -> &mut Self {
157+
self.context = Some(context);
158+
self
159+
}
160+
pub fn spawn_local<F: Future + 'static>(&self, future: F) -> JoinHandle<<F as Future>::Output> {
161+
self.context
162+
.as_ref()
163+
.unwrap_or(&MainContext::default())
164+
.spawn_local_with_priority(self.priority, future)
165+
}
166+
pub fn spawn<R: Send + 'static, F: Future<Output = R> + Send + 'static>(
167+
&self,
168+
future: F,
169+
) -> JoinHandle<R> {
170+
self.context
171+
.as_ref()
172+
.unwrap_or(&MainContext::default())
173+
.spawn_with_priority(self.priority, future)
174+
}
175+
pub fn spawn_from_within<F: Future + 'static>(
176+
&self,
177+
func: impl FnOnce() -> F + Send + 'static,
178+
) -> SpawnWithinJoinHandle<<F as Future>::Output> {
179+
self.context
180+
.as_ref()
181+
.unwrap_or(&MainContext::default())
182+
.spawn_from_within_with_priority(self.priority, func)
183+
}
184+
}
185+
186+
impl From<MainContext> for SpawnOptions {
187+
fn from(value: MainContext) -> Self {
188+
let mut opts = SpawnOptions::new();
189+
opts.context(value);
190+
opts
191+
}
192+
}
193+
194+
// The following trait implementations will reuse the methods from `SpawnOptions`, so the spawned
195+
// futures will have the correct priority chosen by the user.
196+
// This is an improvement compared to `MainContext::spawn_obj`, which doesn't let you specify the
197+
// priority.
198+
impl Spawn for SpawnOptions {
199+
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), futures_task::SpawnError> {
200+
self.spawn(future);
201+
Ok(())
202+
}
203+
}
204+
impl LocalSpawn for SpawnOptions {
205+
fn spawn_local_obj(
206+
&self,
207+
future: LocalFutureObj<'static, ()>,
208+
) -> Result<(), futures_task::SpawnError> {
209+
self.spawn_local(future);
210+
Ok(())
211+
}
212+
}
213+
214+
#[test]
215+
fn test_sleep() {
216+
use crate::MainContext;
217+
218+
let c = MainContext::new();
219+
220+
c.block_on(async {
221+
sleep(Duration::from_millis(10)).await;
222+
sleep(Duration::from_secs(1))
223+
.priority(crate::PRIORITY_HIGH)
224+
.precision(SchedulingPrecision::Second)
225+
.await;
226+
});
227+
}
228+
229+
#[test]
230+
fn test_timeout() {
231+
use crate::{MainContext, MainLoop};
232+
use std::future::ready;
233+
234+
let c = MainContext::new();
235+
let l = MainLoop::new(Some(&c), false);
236+
237+
let tt = timeout(Duration::from_millis(10), ready(()));
238+
let l_clone = l.clone();
239+
c.spawn_local(async move {
240+
tt.await.unwrap();
241+
l_clone.quit();
242+
});
243+
l.run();
244+
245+
let tt = timeout(Duration::from_millis(10), async move { 2 }).priority(crate::PRIORITY_HIGH);
246+
let l_clone = l.clone();
247+
c.spawn(async move {
248+
tt.await.unwrap();
249+
l_clone.quit();
250+
});
251+
l.run();
252+
}
253+
254+
#[test]
255+
fn spawn() {
256+
use crate::{MainContext, MainLoop};
257+
258+
let c = MainContext::new();
259+
let l = MainLoop::new(Some(&c), false);
260+
261+
let l_clone = l.clone();
262+
SpawnOptions::new().spawn(async move {
263+
2;
264+
l_clone.quit();
265+
});
266+
l.run();
267+
268+
let l_clone = l.clone();
269+
SpawnOptions::new()
270+
.context(c)
271+
.priority(crate::PRIORITY_HIGH)
272+
.spawn_local(async move {
273+
2;
274+
l_clone.quit();
275+
});
276+
l.run();
277+
}

glib/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ mod convert;
130130
pub use self::convert::*;
131131
mod enums;
132132
mod functions;
133+
pub mod futures;
133134
pub use self::functions::*;
134135
mod key_file;
135136
pub mod prelude;

0 commit comments

Comments
 (0)