From 1d6b513aa7b3bb20b2f44e4375569fe09400239b Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Sat, 24 Feb 2024 00:28:22 -0800 Subject: [PATCH] feat: setup riven for wasm (nodejs) execution (#63) + use custom `Notify` + use different version of async sleep (tokio vs gloo-timers) + switch futures utils from `tokio` to `futures` --- riven/Cargo.toml | 16 +++++++---- riven/src/config.rs | 4 +-- riven/src/lib.rs | 14 ++++++++++ riven/src/req/mod.rs | 4 +-- riven/src/req/rate_limit.rs | 14 +++++----- riven/src/req/regional_requester.rs | 9 +++--- riven/src/req/token_bucket.rs | 2 +- riven/src/util/mod.rs | 4 ++- riven/src/util/notify.rs | 43 +++++++++++++++++++++++++++++ riven/srcgen/lib.rs.dt | 14 ++++++++++ 10 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 riven/src/util/notify.rs diff --git a/riven/Cargo.toml b/riven/Cargo.toml index 0c900ff..782b68d 100644 --- a/riven/Cargo.toml +++ b/riven/Cargo.toml @@ -11,10 +11,8 @@ include = [ "src/**", "../README.md" ] keywords = [ "riot-games", "riot", "league", "league-of-legends" ] categories = [ "api-bindings", "web-programming::http-client" ] -#[badges] -#travis-ci = { repository = "MingweiSamuel/Riven" } - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["cdylib", "rlib"] [package.metadata.docs.rs] features = [ "nightly" ] @@ -39,6 +37,7 @@ deny-unknown-enum-variants-strings = [] deny-unknown-enum-variants-integers = [] [dependencies] +futures = "0.3" log = "0.4" num_enum = "0.5" parking_lot = "0.12" @@ -49,13 +48,18 @@ serde_json = "1.0" serde_repr = "0.1" strum = "0.20" strum_macros = "0.20" -tokio = { version = "1", default-features = false, features = [ "macros", "parking_lot", "sync", "time" ] } tracing = { version = "0.1", optional = true } +[target.'cfg(not(target_family = "wasm"))'.dependencies] +tokio = { version = "1", default-features = false, features = [ "time" ] } + +[target.'cfg(target_family = "wasm")'.dependencies] +gloo-timers = { version = "0.3", features = [ "futures" ] } +web-time = "1.0.0" + [dev-dependencies] env_logger = "0.10.0" fake_instant = "0.5.0" -futures = "0.3" hyper = { version = "0.14", features = [ "server" ] } tokio = { version = "1", features = [ "full" ] } tokio-shared-rt = "0.1" diff --git a/riven/src/config.rs b/riven/src/config.rs index 258f278..e1c7fc9 100644 --- a/riven/src/config.rs +++ b/riven/src/config.rs @@ -1,9 +1,9 @@ //! Configuration of RiotApi. -use std::time::Duration; - use reqwest::header::{HeaderMap, HeaderValue}; use reqwest::ClientBuilder; +use crate::time::Duration; + /// Configuration for instantiating RiotApi. #[derive(Debug)] pub struct RiotApiConfig { diff --git a/riven/src/lib.rs b/riven/src/lib.rs index 774a0c3..9cef86e 100644 --- a/riven/src/lib.rs +++ b/riven/src/lib.rs @@ -204,3 +204,17 @@ mod riot_api; pub use riot_api::*; mod util; + +/// Wasm compatibility layer for [`std::time`] or [`web_time`]. +#[rustfmt::skip] +pub mod time { + #[cfg(not(target_family = "wasm"))] + pub use std::time::*; + #[cfg(target_family = "wasm")] + pub use web_time::*; + + #[cfg(not(target_family = "wasm"))] + pub use tokio::time::sleep; + #[cfg(target_family = "wasm")] + pub use gloo_timers::future::sleep; +} diff --git a/riven/src/req/mod.rs b/riven/src/req/mod.rs index 3991417..ffd98c6 100644 --- a/riven/src/req/mod.rs +++ b/riven/src/req/mod.rs @@ -4,9 +4,9 @@ mod rate_limit; pub use rate_limit::*; mod rate_limit_type; -use std::time::Instant; - pub use rate_limit_type::*; // Hack for token_bucket_test.rs. + +use crate::time::Instant; mod token_bucket; pub use token_bucket::*; diff --git a/riven/src/req/rate_limit.rs b/riven/src/req/rate_limit.rs index f8e51cf..a118e75 100644 --- a/riven/src/req/rate_limit.rs +++ b/riven/src/req/rate_limit.rs @@ -1,14 +1,15 @@ use std::cmp; -use std::time::{Duration, Instant}; +use futures::FutureExt; use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use reqwest::{Response, StatusCode}; use scan_fmt::scan_fmt; -use tokio::sync::Notify; #[cfg(feature = "tracing")] use tracing as log; use super::{RateLimitType, TokenBucket, VectorTokenBucket}; +use crate::time::{sleep, Duration, Instant}; +use crate::util::Notify; use crate::RiotApiConfig; pub struct RateLimit { @@ -51,11 +52,10 @@ impl RateLimit { pub async fn acquire_both(app_rate_limit: &Self, method_rate_limit: &Self) { while let Some(delay) = Self::acquire_both_or_duration(app_rate_limit, method_rate_limit) { - tokio::select! { - biased; - _ = tokio::time::sleep(delay) => { continue } - _ = app_rate_limit.update_notify.notified() => {} - _ = method_rate_limit.update_notify.notified() => {} + futures::select_biased! { + _ = sleep(delay).fuse() => continue, + _ = method_rate_limit.update_notify.notified().fuse() => {} + _ = app_rate_limit.update_notify.notified().fuse() => {} }; log::trace!("Task awoken due to rate limit update."); } diff --git a/riven/src/req/regional_requester.rs b/riven/src/req/regional_requester.rs index 7dab098..0968ad0 100644 --- a/riven/src/req/regional_requester.rs +++ b/riven/src/req/regional_requester.rs @@ -3,11 +3,10 @@ use std::sync::Arc; use reqwest::{RequestBuilder, StatusCode}; #[cfg(feature = "tracing")] -use tracing as log; -#[cfg(feature = "tracing")] -use tracing::Instrument; +use tracing::{self as log, Instrument}; use super::{RateLimit, RateLimitType}; +use crate::time::{sleep, Duration}; use crate::util::InsertOnlyCHashMap; use crate::{ResponseInfo, Result, RiotApiConfig, RiotApiError}; @@ -113,9 +112,9 @@ impl RegionalRequester { // 1 sec, 2 sec, 4 sec, 8 sec. match retry_after { None => { - let delay = std::time::Duration::from_secs(2_u64.pow(retries as u32)); + let delay = Duration::from_secs(2_u64.pow(retries as u32)); log::debug!("Response {} (retried {} times), NO `retry-after`, using exponential backoff, retrying after {:?}.", status, retries, delay); - let backoff = tokio::time::sleep(delay); + let backoff = sleep(delay); #[cfg(feature = "tracing")] let backoff = backoff.instrument(tracing::info_span!("backoff")); backoff.await; diff --git a/riven/src/req/token_bucket.rs b/riven/src/req/token_bucket.rs index 2384b75..f1825b0 100644 --- a/riven/src/req/token_bucket.rs +++ b/riven/src/req/token_bucket.rs @@ -1,10 +1,10 @@ use std::collections::VecDeque; use std::fmt; -use std::time::Duration; use parking_lot::{Mutex, MutexGuard}; use super::Instant; // Hack for token_bucket_test.rs. +use crate::time::Duration; /// A `TokenBucket` keeps track of number of requests allowed per duration of /// time. diff --git a/riven/src/util/mod.rs b/riven/src/util/mod.rs index 5923333..3e9af15 100644 --- a/riven/src/util/mod.rs +++ b/riven/src/util/mod.rs @@ -1,3 +1,5 @@ mod insert_only_chashmap; +pub use insert_only_chashmap::InsertOnlyCHashMap; -pub use insert_only_chashmap::*; +mod notify; +pub use notify::Notify; diff --git a/riven/src/util/notify.rs b/riven/src/util/notify.rs new file mode 100644 index 0000000..82d4af6 --- /dev/null +++ b/riven/src/util/notify.rs @@ -0,0 +1,43 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll, Waker}; + +use parking_lot::Mutex; + +#[derive(Default)] +pub struct Notify { + waiters: Mutex>, +} +impl Notify { + pub fn new() -> Self { + Self::default() + } + + pub fn notified(&self) -> impl '_ + Future { + struct Notified<'a> { + notify: &'a Notify, + registered: bool, + } + impl Future for Notified<'_> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if std::mem::replace(&mut self.as_mut().registered, true) { + Poll::Ready(()) + } else { + self.notify.waiters.lock().push(cx.waker().clone()); + Poll::Pending + } + } + } + + Notified { + notify: self, + registered: false, + } + } + + pub fn notify_waiters(&self) { + self.waiters.lock().drain(..).for_each(Waker::wake); + } +} diff --git a/riven/srcgen/lib.rs.dt b/riven/srcgen/lib.rs.dt index 1545964..819c371 100644 --- a/riven/srcgen/lib.rs.dt +++ b/riven/srcgen/lib.rs.dt @@ -39,3 +39,17 @@ mod riot_api; pub use riot_api::*; mod util; + +/// Wasm compatibility layer for [`std::time`] or [`web_time`]. +#[rustfmt::skip] +pub mod time { + #[cfg(not(target_family = "wasm"))] + pub use std::time::*; + #[cfg(target_family = "wasm")] + pub use web_time::*; + + #[cfg(not(target_family = "wasm"))] + pub use tokio::time::sleep; + #[cfg(target_family = "wasm")] + pub use gloo_timers::future::sleep; +}