diff --git a/gdnative-async/Cargo.toml b/gdnative-async/Cargo.toml index 109ba8a93..af29417fa 100644 --- a/gdnative-async/Cargo.toml +++ b/gdnative-async/Cargo.toml @@ -16,11 +16,11 @@ edition = "2018" gdnative-derive = { path = "../gdnative-derive", version = "=0.9.3" } gdnative-core = { path = "../gdnative-core", version = "=0.9.3" } gdnative-bindings = { path = "../gdnative-bindings", version = "=0.9.3" } -futures-task = "0.3.13" +futures-task = "0.3.17" atomic-waker = "1.0.0" -once_cell = "1.7.2" -thiserror = "1.0" -parking_lot = "0.11.0" -crossbeam-channel = "0.5.0" +once_cell = "1.8.0" +parking_lot = "0.11.2" +crossbeam-channel = "0.5.1" +crossbeam-utils = "0.8.5" [build-dependencies] diff --git a/gdnative-async/src/executor.rs b/gdnative-async/src/executor.rs index 69afcff1e..df51e3a9a 100644 --- a/gdnative-async/src/executor.rs +++ b/gdnative-async/src/executor.rs @@ -1,45 +1,21 @@ +use std::cell::Cell; + use futures_task::LocalSpawn; -use once_cell::unsync::OnceCell as UnsyncCell; -use thiserror::Error; thread_local!( - static LOCAL_SPAWN: UnsyncCell<&'static dyn LocalSpawn> = UnsyncCell::new(); + static LOCAL_SPAWN: Cell> = Cell::new(None); ); -/// Error returned by `set_*_executor` if an executor of the kind has already been set. -#[derive(Error, Debug)] -#[error("an executor is already set")] -pub struct SetExecutorError { - _private: (), -} - -impl SetExecutorError { - fn new() -> Self { - SetExecutorError { _private: () } - } -} - pub(crate) fn local_spawn() -> Option<&'static dyn LocalSpawn> { - LOCAL_SPAWN.with(|cell| cell.get().copied()) + LOCAL_SPAWN.with(|cell| cell.get()) } /// Sets the global executor for the current thread to a `Box`. This value is leaked. -pub fn set_boxed_executor(sp: Box) -> Result<(), SetExecutorError> { +pub fn set_boxed_executor(sp: Box) { set_executor(Box::leak(sp)) } /// Sets the global executor for the current thread to a `&'static dyn LocalSpawn`. -pub fn set_executor(sp: &'static dyn LocalSpawn) -> Result<(), SetExecutorError> { - LOCAL_SPAWN.with(|cell| cell.set(sp).map_err(|_| SetExecutorError::new())) -} - -/// Sets the global executor for the current thread with a function that will only be called -/// if an executor isn't set yet. -pub fn ensure_executor_with(f: F) -where - F: FnOnce() -> &'static dyn LocalSpawn, -{ - LOCAL_SPAWN.with(|cell| { - cell.get_or_init(f); - }); +pub fn set_executor(sp: &'static dyn LocalSpawn) { + LOCAL_SPAWN.with(|cell| cell.set(Some(sp))) } diff --git a/gdnative-async/src/future.rs b/gdnative-async/src/future.rs index c22eefd8a..d51a1b10f 100644 --- a/gdnative-async/src/future.rs +++ b/gdnative-async/src/future.rs @@ -20,7 +20,8 @@ pub(crate) fn make() -> (Yield, Resume) { (future, resume) } -/// Signal +/// Future that can be `await`ed for a signal or a `resume` call from Godot. See +/// [`Context`](crate::Context) for methods that return this future. pub struct Yield { waker: Arc, arg_recv: Receiver, diff --git a/gdnative-async/src/lib.rs b/gdnative-async/src/lib.rs index b785874b2..cfdb2c77b 100644 --- a/gdnative-async/src/lib.rs +++ b/gdnative-async/src/lib.rs @@ -14,6 +14,7 @@ mod future; mod method; mod rt; -pub use executor::{ensure_executor_with, set_boxed_executor, set_executor, SetExecutorError}; +pub use executor::{set_boxed_executor, set_executor}; +pub use future::Yield; pub use method::{Async, AsyncMethod, Spawner}; -pub use rt::{register_runtime, terminate_runtime, Context, InitError}; +pub use rt::{register_runtime, terminate_runtime, Context}; diff --git a/gdnative-async/src/method.rs b/gdnative-async/src/method.rs index b5dbcd346..a79eb2937 100644 --- a/gdnative-async/src/method.rs +++ b/gdnative-async/src/method.rs @@ -24,8 +24,8 @@ pub trait AsyncMethod: Send + Sync + 'static { /// Spawns the future for result of this method with `spawner`. This is done so /// that implementors of this trait do not have to name their future types. /// - /// If the `spawner` object is not used, the method call will fail, output an error, - /// and return a `Nil` variant. + /// If the `spawner` object is not used, the Godot side of the call will fail, output an + /// error, and return a `Nil` variant. fn spawn_with(&self, spawner: Spawner<'_, C>); /// Returns an optional site where this method is defined. Used for logging errors in FFI wrappers. @@ -37,6 +37,7 @@ pub trait AsyncMethod: Send + Sync + 'static { } } +/// A helper structure for working around naming future types. See [`Spawner::spawn`]. pub struct Spawner<'a, C: NativeClass> { sp: &'static dyn LocalSpawn, ctx: Context, diff --git a/gdnative-async/src/rt.rs b/gdnative-async/src/rt.rs index 3ecd14bf8..21dfeb9de 100644 --- a/gdnative-async/src/rt.rs +++ b/gdnative-async/src/rt.rs @@ -2,8 +2,6 @@ use std::marker::PhantomData; use gdnative_bindings::Object; use gdnative_core::object::SubClass; -use once_cell::sync::OnceCell; -use thiserror::Error; use gdnative_core::core_types::{GodotError, Variant}; use gdnative_core::nativescript::export::InitHandle; @@ -18,20 +16,6 @@ mod func_state; use func_state::FuncState; -static REGISTRATION: OnceCell<()> = OnceCell::new(); - -#[derive(Debug, Error)] -#[error("async runtime must only be initialized once")] -pub struct InitError { - _private: (), -} - -impl InitError { - fn new() -> Self { - InitError { _private: () } - } -} - /// Context for creating `yield`-like futures in async methods. pub struct Context { func_state: Instance, @@ -108,22 +92,15 @@ impl Context { } } -pub fn register_runtime(handle: &InitHandle) -> Result<(), InitError> { - let mut called = false; - - REGISTRATION.get_or_init(|| { - handle.add_class::(); - handle.add_class::(); - called = true; - }); - - if called { - Ok(()) - } else { - Err(InitError::new()) - } +/// Adds required supporting NativeScript classes to `handle`. This must be called once and +/// only once per initialization. +pub fn register_runtime(handle: &InitHandle) { + handle.add_class::(); + handle.add_class::(); } +/// Releases all observers still in use. This should be called in the +/// `godot_gdnative_terminate` callback. pub fn terminate_runtime() { bridge::terminate(); } diff --git a/gdnative-async/src/rt/bridge.rs b/gdnative-async/src/rt/bridge.rs index 861a35896..87a0e979f 100644 --- a/gdnative-async/src/rt/bridge.rs +++ b/gdnative-async/src/rt/bridge.rs @@ -43,8 +43,7 @@ struct Entry { resume: Resume>, // Just need to keep this alive. - #[allow(dead_code)] - obj: Instance, + _obj: Instance, } pub(super) struct SignalBridge { @@ -69,11 +68,6 @@ impl SignalBridge { signal: &str, resume: Resume>, ) -> Result<(), GodotError> { - assert!( - super::REGISTRATION.get().is_some(), - "async API must be registered before any async methods can be called" - ); - let mut pool = BRIDGES.get_or_init(Mutex::default).lock(); let (id, bridge) = pool.free.pop().unwrap_or_else(|| { let id = pool.next_id(); @@ -90,8 +84,8 @@ impl SignalBridge { )?; let entry = Entry { - obj: bridge, resume, + _obj: bridge, }; assert!(pool.busy.insert(id, entry).is_none()); diff --git a/gdnative-async/src/rt/func_state.rs b/gdnative-async/src/rt/func_state.rs index 7ef7b5e32..5e5edb939 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -53,11 +53,6 @@ impl NativeClass for FuncState { impl FuncState { pub fn new() -> Instance { - assert!( - super::REGISTRATION.get().is_some(), - "async API must be registered before any async methods can be called" - ); - Instance::emplace(FuncState { kind: Kind::Pending, }) diff --git a/gdnative/src/lib.rs b/gdnative/src/lib.rs index 3be074ba5..373ffa2a8 100644 --- a/gdnative/src/lib.rs +++ b/gdnative/src/lib.rs @@ -88,4 +88,4 @@ pub use gdnative_bindings as api; #[doc(inline)] #[cfg(feature = "async")] /// Support for async code -pub use gdnative_async as asn; +pub use gdnative_async as tasks; diff --git a/test/src/lib.rs b/test/src/lib.rs index 89432ce69..2ed1cd394 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -272,7 +272,7 @@ fn init(handle: InitHandle) { } fn terminate(_term_info: &gdnative::TerminateInfo) { - gdnative::asn::terminate_runtime(); + gdnative::tasks::terminate_runtime(); } gdnative::macros::godot_gdnative_init!(); diff --git a/test/src/test_async.rs b/test/src/test_async.rs index d5e6f8c02..c2bf792ad 100644 --- a/test/src/test_async.rs +++ b/test/src/test_async.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; -use gdnative::asn::{Async, AsyncMethod, Spawner}; use gdnative::prelude::*; +use gdnative::tasks::{Async, AsyncMethod, Spawner}; pub(crate) fn run_tests() -> bool { // Relevant tests in GDScript @@ -15,8 +15,8 @@ thread_local! { } pub(crate) fn register(handle: InitHandle) { - gdnative::asn::register_runtime(&handle).unwrap(); - gdnative::asn::set_executor(EXECUTOR.with(|e| *e)).unwrap(); + gdnative::tasks::register_runtime(&handle); + gdnative::tasks::set_executor(EXECUTOR.with(|e| *e)); handle.add_class::(); handle.add_class::();