Skip to content

Commit 16f10e9

Browse files
authored
Add a kani::futures library containing block_on (rust-lang#1427)
1 parent 9f972f2 commit 16f10e9

File tree

3 files changed

+64
-12
lines changed

3 files changed

+64
-12
lines changed

library/kani/src/futures.rs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! This module contains functions to work with futures (and async/.await) in Kani.
5+
6+
use std::{
7+
future::Future,
8+
pin::Pin,
9+
task::{Context, RawWaker, RawWakerVTable, Waker},
10+
};
11+
12+
/// A very simple executor: it polls the future in a busy loop until completion
13+
///
14+
/// This is intended as a drop-in replacement for `futures::block_on`, which Kani cannot handle.
15+
/// Whereas a clever executor like `block_on` in `futures` or `tokio` would interact with the OS scheduler
16+
/// to be woken up when a resource becomes available, this is not supported by Kani.
17+
/// As a consequence, this function completely ignores the waker infrastructure and just polls the given future in a busy loop.
18+
pub fn block_on<T>(mut fut: impl Future<Output = T>) -> T {
19+
let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
20+
let cx = &mut Context::from_waker(&waker);
21+
// SAFETY: we shadow the original binding, so it cannot be accessed again for the rest of the scope.
22+
// This is the same as what the pin_mut! macro in the futures crate does.
23+
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
24+
loop {
25+
match fut.as_mut().poll(cx) {
26+
std::task::Poll::Ready(res) => return res,
27+
std::task::Poll::Pending => continue,
28+
}
29+
}
30+
}
31+
32+
/// A dummy waker, which is needed to call [`Future::poll`]
33+
const NOOP_RAW_WAKER: RawWaker = {
34+
#[inline]
35+
unsafe fn clone_waker(_: *const ()) -> RawWaker {
36+
NOOP_RAW_WAKER
37+
}
38+
39+
#[inline]
40+
unsafe fn noop(_: *const ()) {}
41+
42+
RawWaker::new(std::ptr::null(), &RawWakerVTable::new(clone_waker, noop, noop, noop))
43+
};

library/kani/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
#![feature(min_specialization)] // Used for default implementation of Arbitrary.
55

66
pub mod arbitrary;
7+
pub mod futures;
78
pub mod invariant;
89
pub mod slice;
910
pub mod vec;
1011

1112
pub use arbitrary::Arbitrary;
13+
pub use futures::block_on;
1214
pub use invariant::Invariant;
1315

1416
/// Creates an assumption that will be valid after this statement run. Note that the assumption

tests/kani/AsyncAwait/main.rs

+19-12
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,21 @@ use std::{
1414
fn main() {}
1515

1616
#[kani::proof]
17-
#[kani::unwind(10)]
17+
#[kani::unwind(2)]
1818
fn test_async_await() {
19-
poll_loop(async {
19+
// Test using the `block_on` implementation in Kani's library
20+
kani::block_on(async {
21+
let async_block_result = async { 42 }.await;
22+
let async_fn_result = async_fn().await;
23+
assert_eq!(async_block_result, async_fn_result);
24+
})
25+
}
26+
27+
#[kani::proof]
28+
#[kani::unwind(2)]
29+
fn test_async_await_manually() {
30+
// Test using the manual `block_on` implementation
31+
block_on(async {
2032
let async_block_result = async { 42 }.await;
2133
let async_fn_result = async_fn().await;
2234
assert_eq!(async_block_result, async_fn_result);
@@ -28,12 +40,12 @@ pub async fn async_fn() -> i32 {
2840
}
2941

3042
/// A very simple executor that just polls the future in a loop
31-
pub fn poll_loop<F: Future>(mut fut: F) -> <F as Future>::Output {
43+
pub fn block_on<T>(mut fut: impl Future<Output = T>) -> T {
3244
let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
3345
let cx = &mut Context::from_waker(&waker);
46+
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
3447
loop {
35-
let pinned = unsafe { Pin::new_unchecked(&mut fut) };
36-
match pinned.poll(cx) {
48+
match fut.as_mut().poll(cx) {
3749
std::task::Poll::Ready(res) => return res,
3850
std::task::Poll::Pending => continue,
3951
}
@@ -45,11 +57,6 @@ const NOOP_RAW_WAKER: RawWaker = {
4557
unsafe fn clone_waker(_: *const ()) -> RawWaker {
4658
NOOP_RAW_WAKER
4759
}
48-
unsafe fn wake(_: *const ()) {}
49-
unsafe fn wake_by_ref(_: *const ()) {}
50-
unsafe fn drop_waker(_: *const ()) {}
51-
RawWaker::new(
52-
std::ptr::null(),
53-
&RawWakerVTable::new(clone_waker, wake, wake_by_ref, drop_waker),
54-
)
60+
unsafe fn noop(_: *const ()) {}
61+
RawWaker::new(std::ptr::null(), &RawWakerVTable::new(clone_waker, noop, noop, noop))
5562
};

0 commit comments

Comments
 (0)