Skip to content

Commit

Permalink
Fix the nightly futures support (#100)
Browse files Browse the repository at this point in the history
Type Alias Impl Trait (TAIT) seems to now require the type to be
declared in a separate module from where it's used. I don't exactly know
the reasoning for this, but it's fixed now regardless.
  • Loading branch information
CryZe authored Aug 14, 2024
1 parent 83f7de0 commit 086c851
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 41 deletions.
88 changes: 48 additions & 40 deletions src/future/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,25 +345,27 @@ macro_rules! async_main {
#[no_mangle]
pub unsafe extern "C" fn update() {
use core::{
cell::UnsafeCell,
future::Future,
pin::Pin,
ptr,
task::{Context, RawWaker, RawWakerVTable, Waker},
};

type MainFuture = impl Future<Output = ()>;
const fn main_type() -> MainFuture {
async {
main().await;
use $crate::sync::RacyCell;
mod fut {
pub type MainFuture = impl core::future::Future<Output = ()>;
pub const fn main_type() -> MainFuture {
async {
super::main().await;
}
}
}
static mut STATE: MainFuture = main_type();
static mut FINISHED: bool = false;

if unsafe { FINISHED } {
static STATE: RacyCell<fut::MainFuture> = RacyCell::new(fut::main_type());
static FINISHED: RacyCell<bool> = RacyCell::new(false);
if unsafe { *FINISHED.get() } {
return;
}

static VTABLE: RawWakerVTable = RawWakerVTable::new(
|_| RawWaker::new(ptr::null(), &VTABLE),
|_| {},
Expand All @@ -374,7 +376,9 @@ macro_rules! async_main {
let waker = unsafe { Waker::from_raw(raw_waker) };
let mut cx = Context::from_waker(&waker);
unsafe {
FINISHED = Pin::new_unchecked(&mut STATE).poll(&mut cx).is_ready();
*FINISHED.get_mut() = Pin::new_unchecked(&mut *STATE.get_mut())
.poll(&mut cx)
.is_ready();
}
}
};
Expand All @@ -385,17 +389,20 @@ macro_rules! async_main {
#[cfg(target_family = "wasm")]
pub unsafe extern "C" fn update() {
use core::{
cell::UnsafeCell,
future::Future,
mem::{self, ManuallyDrop},
pin::Pin,
ptr,
task::{Context, RawWaker, RawWakerVTable, Waker},
};
use $crate::sync::RacyCell;

static mut STATE: Option<Pin<&'static mut dyn Future<Output = ()>>> = None;
static mut FINISHED: bool = false;
static STATE: RacyCell<Option<Pin<&'static mut dyn Future<Output = ()>>>> =
RacyCell::new(None);
static FINISHED: RacyCell<bool> = RacyCell::new(false);
if unsafe { FINISHED } {
if unsafe { *FINISHED.get() } {
return;
}
Expand All @@ -409,35 +416,36 @@ macro_rules! async_main {
let waker = unsafe { Waker::from_raw(raw_waker) };
let mut cx = Context::from_waker(&waker);
unsafe {
FINISHED = Pin::new_unchecked(STATE.get_or_insert_with(|| {
fn allocate<F: Future<Output = ()> + 'static>(
f: ManuallyDrop<F>,
) -> Pin<&'static mut dyn Future<Output = ()>> {
unsafe {
let size = mem::size_of::<F>();
const PAGE_SIZE: usize = 64 << 10;
assert!(mem::align_of::<F>() <= PAGE_SIZE);
let pages = size.div_ceil(PAGE_SIZE);
#[cfg(target_arch = "wasm32")]
let old_page_count = core::arch::wasm32::memory_grow(0, pages);
#[cfg(target_arch = "wasm64")]
let old_page_count = core::arch::wasm64::memory_grow(0, pages);
let address = old_page_count * PAGE_SIZE;
let ptr = address as *mut ManuallyDrop<F>;
ptr::write(ptr, f);
let ptr = ptr.cast::<F>();
let future: &'static mut F = &mut *ptr;
let future: &'static mut dyn Future<Output = ()> = future;
Pin::static_mut(future)
*FINISHED.get_mut() =
Pin::new_unchecked((&mut *STATE.get_mut()).get_or_insert_with(|| {
fn allocate<F: Future<Output = ()> + 'static>(
f: ManuallyDrop<F>,
) -> Pin<&'static mut dyn Future<Output = ()>> {
unsafe {
let size = mem::size_of::<F>();
const PAGE_SIZE: usize = 64 << 10;
assert!(mem::align_of::<F>() <= PAGE_SIZE);
let pages = size.div_ceil(PAGE_SIZE);
#[cfg(target_arch = "wasm32")]
let old_page_count = core::arch::wasm32::memory_grow(0, pages);
#[cfg(target_arch = "wasm64")]
let old_page_count = core::arch::wasm64::memory_grow(0, pages);
let address = old_page_count * PAGE_SIZE;
let ptr = address as *mut ManuallyDrop<F>;
ptr::write(ptr, f);
let ptr = ptr.cast::<F>();
let future: &'static mut F = &mut *ptr;
let future: &'static mut dyn Future<Output = ()> = future;
Pin::static_mut(future)
}
}
}

allocate(ManuallyDrop::new(main()))
}))
.poll(&mut cx)
.is_ready();
allocate(ManuallyDrop::new(main()))
}))
.poll(&mut cx)
.is_ready();
};
}
};
Expand Down
46 changes: 45 additions & 1 deletion src/sync.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Useful synchronization primitives.
use core::{
cell::{RefCell, RefMut},
cell::{RefCell, RefMut, UnsafeCell},
marker::PhantomData,
ops::{Deref, DerefMut},
};
Expand Down Expand Up @@ -110,3 +110,47 @@ unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
// SAFETY: This is the same as std's MutexGuard, but it can only be safe in
// single-threaded WASM, because we use RefMut underneath.
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}

/// A wrapper type that can be used for creating mutable global variables. It
/// does not by itself provide any thread safety.
#[repr(transparent)]
pub struct RacyCell<T>(UnsafeCell<T>);

// SAFETY: The thread unsafety is delegated to the user of this type.
unsafe impl<T> Sync for RacyCell<T> {}
// SAFETY: The thread unsafety is delegated to the user of this type.
unsafe impl<T> Send for RacyCell<T> {}

impl<T> RacyCell<T> {
/// Creates a new `RacyCell` containing the given value.
#[inline(always)]
pub const fn new(value: T) -> Self {
RacyCell(UnsafeCell::new(value))
}

/// Accesses the inner value as mutable pointer. There is no synchronization
/// provided by this type, so it is up to the user to ensure that no other
/// references to the value are used while this pointer is alive.
///
/// # Safety
///
/// You need to ensure that no other references to the value are used while
/// this pointer is alive.
#[inline(always)]
pub const unsafe fn get_mut(&self) -> *mut T {
self.0.get()
}

/// Accesses the inner value as const pointer. There is no synchronization
/// provided by this type, so it is up to the user to ensure that no other
/// references to the value are used while this pointer is alive.
///
/// # Safety
///
/// You need to ensure that no other references to the value are used while
/// this pointer is alive.
#[inline(always)]
pub const unsafe fn get(&self) -> *const T {
self.0.get()
}
}

0 comments on commit 086c851

Please sign in to comment.