From 30801cabc763b41ec28e78afec15cb469ce8f9ef Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Wed, 14 Aug 2024 18:05:16 +0200 Subject: [PATCH] Fix the nightly futures support 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. --- src/future/mod.rs | 88 ++++++++++++++++++++++++++--------------------- src/sync.rs | 46 ++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 41 deletions(-) diff --git a/src/future/mod.rs b/src/future/mod.rs index d325122..f255429 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -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; - const fn main_type() -> MainFuture { - async { - main().await; + use $crate::sync::RacyCell; + mod fut { + pub type MainFuture = impl core::future::Future; + 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 = RacyCell::new(fut::main_type()); + static FINISHED: RacyCell = RacyCell::new(false); + if unsafe { *FINISHED.get() } { return; } - static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| RawWaker::new(ptr::null(), &VTABLE), |_| {}, @@ -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(); } } }; @@ -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>> = None; - static mut FINISHED: bool = false; + static STATE: RacyCell>>> = + RacyCell::new(None); + static FINISHED: RacyCell = RacyCell::new(false); - if unsafe { FINISHED } { + if unsafe { *FINISHED.get() } { return; } @@ -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 + 'static>( - f: ManuallyDrop, - ) -> Pin<&'static mut dyn Future> { - unsafe { - let size = mem::size_of::(); - const PAGE_SIZE: usize = 64 << 10; - assert!(mem::align_of::() <= 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; - ptr::write(ptr, f); - let ptr = ptr.cast::(); - let future: &'static mut F = &mut *ptr; - let future: &'static mut dyn Future = future; - Pin::static_mut(future) + *FINISHED.get_mut() = + Pin::new_unchecked((&mut *STATE.get_mut()).get_or_insert_with(|| { + fn allocate + 'static>( + f: ManuallyDrop, + ) -> Pin<&'static mut dyn Future> { + unsafe { + let size = mem::size_of::(); + const PAGE_SIZE: usize = 64 << 10; + assert!(mem::align_of::() <= 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; + ptr::write(ptr, f); + let ptr = ptr.cast::(); + let future: &'static mut F = &mut *ptr; + let future: &'static mut dyn Future = future; + Pin::static_mut(future) + } } - } - allocate(ManuallyDrop::new(main())) - })) - .poll(&mut cx) - .is_ready(); + allocate(ManuallyDrop::new(main())) + })) + .poll(&mut cx) + .is_ready(); }; } }; diff --git a/src/sync.rs b/src/sync.rs index cf96caa..e3de05d 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,7 +1,7 @@ //! Useful synchronization primitives. use core::{ - cell::{RefCell, RefMut}, + cell::{RefCell, RefMut, UnsafeCell}, marker::PhantomData, ops::{Deref, DerefMut}, }; @@ -110,3 +110,47 @@ unsafe impl Sync for Mutex {} // 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 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(UnsafeCell); + +// SAFETY: The thread unsafety is delegated to the user of this type. +unsafe impl Sync for RacyCell {} +// SAFETY: The thread unsafety is delegated to the user of this type. +unsafe impl Send for RacyCell {} + +impl RacyCell { + /// 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() + } +}