Skip to content

Commit

Permalink
Merge pull request #35 from acterglobal/ben-merging-futures
Browse files Browse the repository at this point in the history
Backporting futures
  • Loading branch information
gnunicorn authored Jun 4, 2024
2 parents 434d2c7 + 0e854cc commit c7dc633
Show file tree
Hide file tree
Showing 14 changed files with 605 additions and 553 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Dart frontend for UniFFI bindings

Reference: [TODOs](./TODO.md)

## MSRV: 1.1.70
## MSRV: 1.74

This project must always work on latest stable rust + version before. We are also testing it against 1.1.70.0 , which we consider the Minimum Support Rust Version (MSRV) at this point. Rust lower than that will probably not compile the project.

Expand Down
2 changes: 1 addition & 1 deletion fixtures/futures/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
fn main() {
// uniffi_dart::generate_scaffolding("./src/api.udl".into()).unwrap();
uniffi_dart::generate_scaffolding("./src/api.udl".into()).unwrap();
}
360 changes: 180 additions & 180 deletions fixtures/futures/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,162 +1,163 @@
// use uniffi;

// use std::{
// future::Future,
// pin::Pin,
// sync::{Arc, Mutex, MutexGuard},
// task::{Context, Poll, Waker},
// thread,
// time::Duration,
// };
use uniffi;

use std::{
future::Future,
pin::Pin,
sync::{Arc, Mutex, MutexGuard},
task::{Context, Poll, Waker},
thread,
time::Duration,
};

/// Non-blocking timer future.
pub struct TimerFuture {
shared_state: Arc<Mutex<SharedState>>,
}

struct SharedState {
completed: bool,
waker: Option<Waker>,
}

impl Future for TimerFuture {
type Output = ();

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut shared_state = self.shared_state.lock().unwrap();

if shared_state.completed {
Poll::Ready(())
} else {
shared_state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}

impl TimerFuture {
pub fn new(duration: Duration) -> Self {
let shared_state = Arc::new(Mutex::new(SharedState {
completed: false,
waker: None,
}));

let thread_shared_state = shared_state.clone();

// Let's mimic an event coming from somewhere else, like the system.
thread::spawn(move || {
thread::sleep(duration);

let mut shared_state: MutexGuard<_> = thread_shared_state.lock().unwrap();
shared_state.completed = true;

if let Some(waker) = shared_state.waker.take() {
waker.wake();
}
});

Self { shared_state }
}
}

// /// Non-blocking timer future.
// pub struct TimerFuture {
// shared_state: Arc<Mutex<SharedState>>,
// }

// struct SharedState {
// completed: bool,
// waker: Option<Waker>,
// }

// impl Future for TimerFuture {
// type Output = ();

// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// let mut shared_state = self.shared_state.lock().unwrap();

// if shared_state.completed {
// Poll::Ready(())
// } else {
// shared_state.waker = Some(cx.waker().clone());
// Poll::Pending
// }
// }
// }

// impl TimerFuture {
// pub fn new(duration: Duration) -> Self {
// let shared_state = Arc::new(Mutex::new(SharedState {
// completed: false,
// waker: None,
// }));

// let thread_shared_state = shared_state.clone();

// // Let's mimic an event coming from somewhere else, like the system.
// thread::spawn(move || {
// thread::sleep(duration);

// let mut shared_state: MutexGuard<_> = thread_shared_state.lock().unwrap();
// shared_state.completed = true;

// if let Some(waker) = shared_state.waker.take() {
// waker.wake();
// }
// });

// Self { shared_state }
// }
// }

// /// Non-blocking timer future.
// pub struct BrokenTimerFuture {
// shared_state: Arc<Mutex<SharedState>>,
// }

// impl Future for BrokenTimerFuture {
// type Output = ();

// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// let mut shared_state = self.shared_state.lock().unwrap();

// if shared_state.completed {
// Poll::Ready(())
// } else {
// shared_state.waker = Some(cx.waker().clone());
// Poll::Pending
// }
// }
// }

// impl BrokenTimerFuture {
// pub fn new(duration: Duration, fail_after: Duration) -> Self {
// let shared_state = Arc::new(Mutex::new(SharedState {
// completed: false,
// waker: None,
// }));

// let thread_shared_state = shared_state.clone();

// // Let's mimic an event coming from somewhere else, like the system.
// thread::spawn(move || {
// thread::sleep(duration);

// let mut shared_state: MutexGuard<_> = thread_shared_state.lock().unwrap();
// shared_state.completed = true;

// if let Some(waker) = shared_state.waker.take() {
// // Do not consume `waker`.
// waker.wake_by_ref();

// // And this is the important part. We are going to call
// // `wake()` a second time. That's incorrect, but that's on
// // purpose, to see how foreign languages will react.
// if fail_after.is_zero() {
// waker.wake();
// } else {
// thread::spawn(move || {
// thread::sleep(fail_after);
// waker.wake();
// });
// }
// }
// });

// Self { shared_state }
// }
// }

// #[uniffi::export]
// pub fn greet(who: String) -> String {
// format!("Hello, {who}")
// }

// #[uniffi::export]
// pub async fn always_ready() -> bool {
// true
// }

// #[uniffi::export]
// pub async fn void_function() {}

// #[uniffi::export]
// pub async fn say() -> String {
// TimerFuture::new(Duration::from_secs(2)).await;

// "Hello, Future!".to_string()
// }

// #[uniffi::export]
// pub async fn say_after(ms: u16, who: String) -> String {
// TimerFuture::new(Duration::from_millis(ms.into())).await;

// format!("Hello, {who}!")
// }

// #[uniffi::export]
// pub async fn sleep(ms: u16) -> bool {
// TimerFuture::new(Duration::from_millis(ms.into())).await;

// true
// }

// // Our error.
pub struct BrokenTimerFuture {
shared_state: Arc<Mutex<SharedState>>,
}

impl Future for BrokenTimerFuture {
type Output = ();

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut shared_state = self.shared_state.lock().unwrap();

if shared_state.completed {
Poll::Ready(())
} else {
shared_state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}

impl BrokenTimerFuture {
pub fn new(duration: Duration, fail_after: Duration) -> Self {
let shared_state = Arc::new(Mutex::new(SharedState {
completed: false,
waker: None,
}));

let thread_shared_state = shared_state.clone();

// Let's mimic an event coming from somewhere else, like the system.
thread::spawn(move || {
thread::sleep(duration);

let mut shared_state: MutexGuard<_> = thread_shared_state.lock().unwrap();
shared_state.completed = true;

if let Some(waker) = shared_state.waker.take() {
// Do not consume `waker`.
waker.wake_by_ref();

// And this is the important part. We are going to call
// `wake()` a second time. That's incorrect, but that's on
// purpose, to see how foreign languages will react.
if fail_after.is_zero() {
waker.wake();
} else {
thread::spawn(move || {
thread::sleep(fail_after);
waker.wake();
});
}
}
});

Self { shared_state }
}
}

#[uniffi::export]
pub fn greet(who: String) -> String {
format!("Hello, {who}")
}

#[uniffi::export]
pub async fn always_ready() -> bool {
true
}

#[uniffi::export]
pub async fn void_function() {}

#[uniffi::export]
pub async fn say() -> String {
TimerFuture::new(Duration::from_secs(2)).await;

"Hello, Future!".to_string()
}

#[uniffi::export]
pub async fn say_after(ms: u16, who: String) -> String {
TimerFuture::new(Duration::from_millis(ms.into())).await;

format!("Hello, {who}!")
}

#[uniffi::export]
pub async fn sleep(ms: u16) -> bool {
TimerFuture::new(Duration::from_millis(ms.into())).await;

true
}

// Our error.
// #[derive(uniffi::Error, Debug)]
// pub enum MyError {
// Foo,
// }

// // An async function that can throw.
// // An async function that can throw.
// #[uniffi::export]
// pub async fn fallible_me(do_fail: bool) -> Result<u8, MyError> {
Expand All @@ -167,31 +168,30 @@
// }
// }

// #[uniffi::export(async_runtime = "tokio")]
// pub async fn say_after_with_tokio(ms: u16, who: String) -> String {
// tokio::time::sleep(Duration::from_millis(ms.into())).await;

// format!("Hello, {who} (with Tokio)!")
// }

// #[derive(uniffi::Record)]
// pub struct MyRecord {
// pub a: String,
// pub b: u32,
// }

// #[uniffi::export]
// pub async fn new_my_record(a: String, b: u32) -> MyRecord {
// MyRecord { a, b }
// }

// #[uniffi::export]
// pub async fn broken_sleep(ms: u16, fail_after: u16) {
// BrokenTimerFuture::new(
// Duration::from_millis(ms.into()),
// Duration::from_millis(fail_after.into()),
// )
// .await;
// }

// uniffi::include_scaffolding!("api");
#[uniffi::export(async_runtime = "tokio")]
pub async fn say_after_with_tokio(ms: u16, who: String) -> String {
tokio::time::sleep(Duration::from_millis(ms.into())).await;
format!("Hello, {who} (with Tokio)!")
}

#[derive(uniffi::Record, Clone)]
pub struct MyRecord {
pub a: String,
pub b: u32,
}

#[uniffi::export]
pub async fn new_my_record(a: String, b: u32) -> MyRecord {
MyRecord { a, b }
}

#[uniffi::export]
pub async fn broken_sleep(ms: u16, fail_after: u16) {
BrokenTimerFuture::new(
Duration::from_millis(ms.into()),
Duration::from_millis(fail_after.into()),
)
.await;
}

uniffi::include_scaffolding!("api");
Loading

0 comments on commit c7dc633

Please sign in to comment.