1use std::{
2 cell::Cell,
3 future::Future,
4 pin::Pin,
5 ptr,
6 task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
7 thread,
8 time::Duration,
9};
1011use crate::Error;
1213const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
14// Cloning just returns a new no-op raw waker
15|_| NOOP_RAW_WAKER,
16// `wake` does nothing
17|_| {},
18// `wake_by_ref` does nothing
19|_| {},
20// Dropping does nothing as we don't allocate anything
21|_| {},
22);
23const NOOP_RAW_WAKER: RawWaker = RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE);
2425#[derive(Default)]
26pub(crate) struct YieldOnce(bool);
2728impl Future for YieldOnce {
29type Output = ();
3031fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
32let flag = &mut std::pin::Pin::into_inner(self).0;
33if !*flag {
34*flag = true;
35 Poll::Pending
36 } else {
37 Poll::Ready(())
38 }
39 }
40}
4142/// Execute the futures and return when they are all done.
43///
44/// Here we use our own homebrew async executor since cc is used in the build
45/// script of many popular projects, pulling in additional dependencies would
46/// significantly slow down its compilation.
47pub(crate) fn block_on<Fut1, Fut2>(
48mut fut1: Fut1,
49mut fut2: Fut2,
50 has_made_progress: &Cell<bool>,
51) -> Result<(), Error>
52where
53Fut1: Future<Output = Result<(), Error>>,
54 Fut2: Future<Output = Result<(), Error>>,
55{
56// Shadows the future so that it can never be moved and is guaranteed
57 // to be pinned.
58 //
59 // The same trick used in `pin!` macro.
60 //
61 // TODO: Once MSRV is bumped to 1.68, replace this with `std::pin::pin!`
62let mut fut1 = Some(unsafe { Pin::new_unchecked(&mut fut1) });
63let mut fut2 = Some(unsafe { Pin::new_unchecked(&mut fut2) });
6465// TODO: Once `Waker::noop` stablised and our MSRV is bumped to the version
66 // which it is stablised, replace this with `Waker::noop`.
67let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
68let mut context = Context::from_waker(&waker);
6970let mut backoff_cnt = 0;
7172loop {
73 has_made_progress.set(false);
7475if let Some(fut) = fut2.as_mut() {
76if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
77 fut2 = None;
78 res?;
79 }
80 }
8182if let Some(fut) = fut1.as_mut() {
83if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
84 fut1 = None;
85 res?;
86 }
87 }
8889if fut1.is_none() && fut2.is_none() {
90return Ok(());
91 }
9293if !has_made_progress.get() {
94if backoff_cnt > 3 {
95// We have yielded at least three times without making'
96 // any progress, so we will sleep for a while.
97let duration = Duration::from_millis(100 * (backoff_cnt - 3).min(10));
98 thread::sleep(duration);
99 } else {
100// Given that we spawned a lot of compilation tasks, it is unlikely
101 // that OS cannot find other ready task to execute.
102 //
103 // If all of them are done, then we will yield them and spawn more,
104 // or simply return.
105 //
106 // Thus this will not be turned into a busy-wait loop and it will not
107 // waste CPU resource.
108thread::yield_now();
109 }
110 }
111112 backoff_cnt = if has_made_progress.get() {
1130
114} else {
115 backoff_cnt + 1
116};
117 }
118}