I'd like to be able to pin a single, known future in static memory, and be able to poll it from a "notified" function that's handled by a external (async, but not rust async) executor-like loop of seL4 microkit.
In addition, I'd rather not have to allocate memory, as all the existing C code in this model don't need memory allocation at runtime.
I believe that on stable rust, my two options seem to be Box::pin
(heap) or the pin!
macro (stack). As I don't control the stack¹, I can't pin!
, and I'd rather avoid heap allocation when it's a single known future.
What follows is the reasoning of the code, which is what I'm asking for feedback on, and if there's a better way to do it.
- To be able to store the future in a static, we need
TAIT
nightly feature, which seems(?) to require anothermod future
module to do the type-inference - To be able to store the future in statics, we need
const_async_blocks
. - Since we can't directly create an owned-pinned value, we need a second
static
for storing the future, and to usePin::static_mut
we need a'static
lifetime, so we can't putstatic mut STORAGE
inside of aRefCell
etc. So there's anunsafe
block that takes it to a pointer and back (because otherwise rustc complains about&mut STORAGE
references).
Is there a way to do this without nightly? Some better feature I'm just missing?
The "working" implementation that you can just run is here. Following is a simple implementation for the rust playground that builds.
¹ Whilst I could get this working by rewriting the C code, there's a fair amount of resistance to this, not to mention that the existing C code has been formally verified and that work would have to be redone.
As a slightly secondary question, in the repo I need -Z build-std
— as otherwise the linker complains about a missing rust_eh_personality
symbol. Is that the appropriate fix?
#![no_std]
#![feature(type_alias_impl_trait, const_async_blocks)]
#![feature(thread_local)]
#![deny(clippy::undocumented_unsafe_blocks)]
use core::{cell::RefCell, convert::Infallible, future::Future, pin::Pin};
async fn mainer() -> ! {
/* Pretend this actually does something useful */
loop {}
}
#[thread_local]
static FUTURE: RefCell<Pin<&mut dyn Future<Output = Infallible>>> = {
static mut STORAGE: future::TheFuture = future::run_main();
// SAFETY:
// - The mut STORAGE is only ever &mut referenced once, by us, and we hide
// it within this block, so aliasing is OK.
// - It is a valid pointer, because we made the source itself.
RefCell::new(Pin::static_mut(unsafe {
(&raw mut STORAGE)
.as_mut()
.expect("ptr is valid + this is the only reference")
}))
};
fn poll_task(_: Pin<&mut dyn Future<Output = Infallible>>) {
/* pretend this actually polls */
}
#[no_mangle]
pub extern "C" fn notified(/* some arguments */) {
poll_task(FUTURE.borrow_mut().as_mut());
}
mod future {
use super::*;
pub type TheFuture = impl Future<Output = Infallible>;
pub const fn run_main() -> TheFuture {
async {
mainer().await;
}
}
}
Errors:
Compiling playground v0.0.1 (/playground)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.36s