diff --git a/veilid-core/src/network_manager/send_data.rs b/veilid-core/src/network_manager/send_data.rs index c7643984..2ebe75ac 100644 --- a/veilid-core/src/network_manager/send_data.rs +++ b/veilid-core/src/network_manager/send_data.rs @@ -381,8 +381,7 @@ impl NetworkManager { .with_address_type_set(peer_a.signed_node_info().node_info().address_types()) .with_protocol_type_set(peer_a.signed_node_info().node_info().outbound_protocols())); let sequencing = target_node_ref.sequencing(); - - + // If the node has had lost questions or failures to send, prefer sequencing // to improve reliability. The node may be experiencing UDP fragmentation drops // or other firewalling issues and may perform better with TCP. diff --git a/veilid-core/src/routing_table/route_spec_store/mod.rs b/veilid-core/src/routing_table/route_spec_store/mod.rs index ccae2927..ca779e0f 100644 --- a/veilid-core/src/routing_table/route_spec_store/mod.rs +++ b/veilid-core/src/routing_table/route_spec_store/mod.rs @@ -166,7 +166,7 @@ impl RouteSpecStore { /// Returns Err(VeilidAPIError::TryAgain) if no route could be allocated at this time /// Returns other errors on failure /// Returns Ok(route id string) on success - #[instrument(level = "trace", skip(self), ret, err)] + #[instrument(level = "trace", skip(self), ret, err(level=Level::TRACE))] pub fn allocate_route( &self, crypto_kinds: &[CryptoKind], @@ -192,7 +192,7 @@ impl RouteSpecStore { ) } - #[instrument(level = "trace", skip(self, inner, rti), ret, err(level=Level::DEBUG))] + #[instrument(level = "trace", skip(self, inner, rti), ret, err(level=Level::TRACE))] #[allow(clippy::too_many_arguments)] fn allocate_route_inner( &self, diff --git a/veilid-core/src/routing_table/tasks/bootstrap.rs b/veilid-core/src/routing_table/tasks/bootstrap.rs index aef1180c..ceebe7a8 100644 --- a/veilid-core/src/routing_table/tasks/bootstrap.rs +++ b/veilid-core/src/routing_table/tasks/bootstrap.rs @@ -252,56 +252,106 @@ impl RoutingTable { Ok(merged_bootstrap_records) } - // 'direct' bootstrap task routine for systems incapable of resolving TXT records, such as browser WASM - #[instrument(level = "trace", skip(self), err)] - pub(crate) async fn direct_bootstrap_task_routine( - self, - stop_token: StopToken, - bootstrap_dialinfos: Vec, - ) -> EyreResult<()> { - let mut unord = FuturesUnordered::new(); - let network_manager = self.network_manager(); + //#[instrument(level = "trace", skip(self), err)] + pub(crate) fn bootstrap_with_peer(self, crypto_kinds: Vec, pi: PeerInfo, unord: &FuturesUnordered>) { - for bootstrap_di in bootstrap_dialinfos { - log_rtab!(debug "direct bootstrap with: {}", bootstrap_di); - let peer_info = network_manager.boot_request(bootstrap_di).await?; + log_rtab!( + "--- bootstrapping {} with {:?}", + pi.node_ids(), + pi.signed_node_info().node_info().dial_info_detail_list() + ); - log_rtab!(debug " direct bootstrap peerinfo: {:?}", peer_info); - - // Got peer info, let's add it to the routing table - for pi in peer_info { - // Register the node - let nr = match self.register_node_with_peer_info( - RoutingDomain::PublicInternet, - pi, - false, - ) { - Ok(nr) => nr, - Err(e) => { - log_rtab!(error "failed to register direct bootstrap peer info: {}", e); - continue; - } - }; - - // Add this our futures to process in parallel - for crypto_kind in VALID_CRYPTO_KINDS { - let routing_table = self.clone(); - let nr = nr.clone(); - unord.push( - // lets ask bootstrap to find ourselves now - async move { routing_table.reverse_find_node(crypto_kind, nr, true).await } - .instrument(Span::current()), - ); - } + let nr = + match self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, true) { + Ok(nr) => nr, + Err(e) => { + log_rtab!(error "failed to register bootstrap peer info: {}", e); + return; } + }; + + // Add this our futures to process in parallel + for crypto_kind in crypto_kinds { + + // Bootstrap this crypto kind + let nr = nr.clone(); + let routing_table = self.clone(); + unord.push(Box::pin( + async move { + // Get what contact method would be used for contacting the bootstrap + let bsdi = match routing_table + .network_manager() + .get_node_contact_method(nr.clone()) + { + Ok(NodeContactMethod::Direct(v)) => v, + Ok(v) => { + log_rtab!(warn "invalid contact method for bootstrap: {:?}", v); + return; + } + Err(e) => { + log_rtab!(warn "unable to bootstrap: {}", e); + return; + } + }; + + // Need VALID signed peer info, so ask bootstrap to find_node of itself + // which will ensure it has the bootstrap's signed peer info as part of the response + let _ = routing_table.find_target(crypto_kind, nr.clone()).await; + + // Ensure we got the signed peer info + if !nr.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet) { + log_rtab!(warn "bootstrap server is not responding"); + log_rtab!(debug "bootstrap server is not responding for dialinfo: {}", bsdi); + + // Try a different dialinfo next time + routing_table.network_manager().address_filter().set_dial_info_failed(bsdi); + } else { + // otherwise this bootstrap is valid, lets ask it to find ourselves now + routing_table.reverse_find_node(crypto_kind, nr, true).await + } + } + .instrument(Span::current()), + )); + } + } + + #[instrument(level = "trace", skip(self), err)] + pub(crate) async fn bootstrap_with_peer_list(self, peers: Vec, stop_token: StopToken) -> EyreResult<()> { + + log_rtab!(debug " bootstrapped peers: {:?}", &peers); + + // Get crypto kinds to bootstrap + let crypto_kinds = self.get_bootstrap_crypto_kinds(); + + log_rtab!(debug " bootstrapped crypto kinds: {:?}", &crypto_kinds); + + // Run all bootstrap operations concurrently + let mut unord = FuturesUnordered::>::new(); + for peer in peers { + self.clone().bootstrap_with_peer(crypto_kinds.clone(), peer, &unord); } // Wait for all bootstrap operations to complete before we complete the singlefuture while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {} - Ok(()) } + // Get counts by crypto kind and figure out which crypto kinds need bootstrapping + fn get_bootstrap_crypto_kinds(&self) -> Vec { + let entry_count = self.inner.read().cached_entry_counts(); + let mut crypto_kinds = Vec::new(); + for crypto_kind in VALID_CRYPTO_KINDS { + // Do we need to bootstrap this crypto kind? + let eckey = (RoutingDomain::PublicInternet, crypto_kind); + let cnt = entry_count.get(&eckey).copied().unwrap_or_default(); + if cnt == 0 { + crypto_kinds.push(crypto_kind); + } + } + crypto_kinds + } + + #[instrument(level = "trace", skip(self), err)] pub(crate) async fn bootstrap_task_routine(self, stop_token: StopToken) -> EyreResult<()> { let bootstrap = self @@ -315,9 +365,6 @@ impl RoutingTable { log_rtab!(debug "--- bootstrap_task"); - // Get counts by crypto kind - let entry_count = self.inner.read().cached_entry_counts(); - // See if we are specifying a direct dialinfo for bootstrap, if so use the direct mechanism let mut bootstrap_dialinfos = Vec::::new(); for b in &bootstrap { @@ -327,102 +374,48 @@ impl RoutingTable { } } } - if !bootstrap_dialinfos.is_empty() { - return self - .direct_bootstrap_task_routine(stop_token, bootstrap_dialinfos) - .await; - } + + // Get a peer list from bootstrap to process + let peers = if !bootstrap_dialinfos.is_empty() { + // Direct bootstrap + let network_manager = self.network_manager(); - // If not direct, resolve bootstrap servers and recurse their TXT entries - let bsrecs = self.resolve_bootstrap(bootstrap).await?; - - // Run all bootstrap operations concurrently - let mut unord = FuturesUnordered::new(); - for bsrec in bsrecs { - log_rtab!( - "--- bootstrapping {} with {:?}", - &bsrec.node_ids, - &bsrec.dial_info_details - ); - - // Get crypto support from list of node ids - let crypto_support = bsrec.node_ids.kinds(); - - // Make unsigned SignedNodeInfo - let sni = - SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo::new( - NetworkClass::InboundCapable, // Bootstraps are always inbound capable - ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled - AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable - bsrec.envelope_support, // Envelope support is as specified in the bootstrap list - crypto_support, // Crypto support is derived from list of node ids - vec![], // Bootstrap needs no capabilities - bsrec.dial_info_details, // Dial info is as specified in the bootstrap list - ))); - - let pi = PeerInfo::new(bsrec.node_ids, sni); - - let nr = - match self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, true) { - Ok(nr) => nr, - Err(e) => { - log_rtab!(error "failed to register bootstrap peer info: {}", e); - continue; + let mut peer_map = HashMap::::new(); + for bootstrap_di in bootstrap_dialinfos { + log_rtab!(debug "direct bootstrap with: {}", bootstrap_di); + let peers = network_manager.boot_request(bootstrap_di).await?; + for peer in peers { + if !peer_map.contains_key(peer.node_ids()) { + peer_map.insert(peer.node_ids().clone(), peer); } - }; - // Add this our futures to process in parallel - for crypto_kind in VALID_CRYPTO_KINDS { - // Do we need to bootstrap this crypto kind? - let eckey = (RoutingDomain::PublicInternet, crypto_kind); - let cnt = entry_count.get(&eckey).copied().unwrap_or_default(); - if cnt != 0 { - continue; } - - // Bootstrap this crypto kind - let nr = nr.clone(); - let routing_table = self.clone(); - unord.push( - async move { - // Get what contact method would be used for contacting the bootstrap - let bsdi = match routing_table - .network_manager() - .get_node_contact_method(nr.clone()) - { - Ok(NodeContactMethod::Direct(v)) => v, - Ok(v) => { - log_rtab!(warn "invalid contact method for bootstrap: {:?}", v); - return; - } - Err(e) => { - log_rtab!(warn "unable to bootstrap: {}", e); - return; - } - }; - - // Need VALID signed peer info, so ask bootstrap to find_node of itself - // which will ensure it has the bootstrap's signed peer info as part of the response - let _ = routing_table.find_target(crypto_kind, nr.clone()).await; - - // Ensure we got the signed peer info - if !nr.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet) { - log_rtab!(warn "bootstrap server is not responding"); - log_rtab!(debug "bootstrap server is not responding for dialinfo: {}", bsdi); - - // Try a different dialinfo next time - routing_table.network_manager().address_filter().set_dial_info_failed(bsdi); - } else { - // otherwise this bootstrap is valid, lets ask it to find ourselves now - routing_table.reverse_find_node(crypto_kind, nr, true).await - } - } - .instrument(Span::current()), - ); } - } + peer_map.into_values().collect() + } else { + // If not direct, resolve bootstrap servers and recurse their TXT entries + let bsrecs = self.resolve_bootstrap(bootstrap).await?; + let peers : Vec = bsrecs.into_iter().map(|bsrec| { + // Get crypto support from list of node ids + let crypto_support = bsrec.node_ids.kinds(); - // Wait for all bootstrap operations to complete before we complete the singlefuture - while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {} - Ok(()) + // Make unsigned SignedNodeInfo + let sni = + SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo::new( + NetworkClass::InboundCapable, // Bootstraps are always inbound capable + ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled + AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable + bsrec.envelope_support, // Envelope support is as specified in the bootstrap list + crypto_support, // Crypto support is derived from list of node ids + vec![], // Bootstrap needs no capabilities + bsrec.dial_info_details, // Dial info is as specified in the bootstrap list + ))); + + PeerInfo::new(bsrec.node_ids, sni) + }).collect(); + + peers + }; + + self.clone().bootstrap_with_peer_list(peers, stop_token).await } } diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index 0a1530c4..2115ed58 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -181,10 +181,10 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn { } "network.connection_initial_timeout_ms" => Ok(Box::new(2_000u32)), "network.connection_inactivity_timeout_ms" => Ok(Box::new(60_000u32)), - "network.max_connections_per_ip4" => Ok(Box::new(8u32)), - "network.max_connections_per_ip6_prefix" => Ok(Box::new(8u32)), + "network.max_connections_per_ip4" => Ok(Box::new(32u32)), + "network.max_connections_per_ip6_prefix" => Ok(Box::new(32u32)), "network.max_connections_per_ip6_prefix_size" => Ok(Box::new(56u32)), - "network.max_connection_frequency_per_min" => Ok(Box::new(8u32)), + "network.max_connection_frequency_per_min" => Ok(Box::new(128u32)), "network.client_whitelist_timeout_ms" => Ok(Box::new(300_000u32)), "network.reverse_connection_receipt_time_ms" => Ok(Box::new(5_000u32)), "network.hole_punch_receipt_time_ms" => Ok(Box::new(5_000u32)), @@ -203,7 +203,7 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn { "network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)), "network.routing_table.limit_attached_good" => Ok(Box::new(8u32)), "network.routing_table.limit_attached_weak" => Ok(Box::new(4u32)), - "network.rpc.concurrency" => Ok(Box::new(2u32)), + "network.rpc.concurrency" => Ok(Box::new(0u32)), "network.rpc.queue_size" => Ok(Box::new(1024u32)), "network.rpc.max_timestamp_behind_ms" => Ok(Box::new(Some(10_000u32))), "network.rpc.max_timestamp_ahead_ms" => Ok(Box::new(Some(10_000u32))), @@ -222,7 +222,7 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn { "network.dht.set_value_fanout" => Ok(Box::new(4u32)), "network.dht.min_peer_count" => Ok(Box::new(20u32)), "network.dht.min_peer_refresh_time_ms" => Ok(Box::new(60_000u32)), - "network.dht.validate_dial_info_receipt_time_ms" => Ok(Box::new(5_000u32)), + "network.dht.validate_dial_info_receipt_time_ms" => Ok(Box::new(2_000u32)), "network.dht.local_subkey_cache_size" => Ok(Box::new(128u32)), "network.dht.local_max_subkey_cache_memory_mb" => Ok(Box::new(256u32)), "network.dht.remote_subkey_cache_size" => Ok(Box::new(1024u32)), @@ -231,7 +231,7 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn { "network.dht.remote_max_storage_space_mb" => Ok(Box::new(64u32)), "network.upnp" => Ok(Box::new(false)), "network.detect_address_changes" => Ok(Box::new(true)), - "network.restricted_nat_retries" => Ok(Box::new(3u32)), + "network.restricted_nat_retries" => Ok(Box::new(0u32)), "network.tls.certificate_path" => Ok(Box::new(get_certfile_path())), "network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())), "network.tls.connection_initial_timeout_ms" => Ok(Box::new(2_000u32)), @@ -244,7 +244,7 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn { "network.application.http.path" => Ok(Box::new(String::from("app"))), "network.application.http.url" => Ok(Box::new(Option::::None)), "network.protocol.udp.enabled" => Ok(Box::new(true)), - "network.protocol.udp.socket_pool_size" => Ok(Box::new(16u32)), + "network.protocol.udp.socket_pool_size" => Ok(Box::new(0u32)), "network.protocol.udp.listen_address" => Ok(Box::new("".to_owned())), "network.protocol.udp.public_address" => Ok(Box::new(Option::::None)), "network.protocol.tcp.connect" => Ok(Box::new(true)), @@ -252,15 +252,15 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn { "network.protocol.tcp.max_connections" => Ok(Box::new(32u32)), "network.protocol.tcp.listen_address" => Ok(Box::new("".to_owned())), "network.protocol.tcp.public_address" => Ok(Box::new(Option::::None)), - "network.protocol.ws.connect" => Ok(Box::new(false)), - "network.protocol.ws.listen" => Ok(Box::new(false)), - "network.protocol.ws.max_connections" => Ok(Box::new(16u32)), + "network.protocol.ws.connect" => Ok(Box::new(true)), + "network.protocol.ws.listen" => Ok(Box::new(true)), + "network.protocol.ws.max_connections" => Ok(Box::new(32u32)), "network.protocol.ws.listen_address" => Ok(Box::new("".to_owned())), "network.protocol.ws.path" => Ok(Box::new(String::from("ws"))), "network.protocol.ws.url" => Ok(Box::new(Option::::None)), - "network.protocol.wss.connect" => Ok(Box::new(false)), + "network.protocol.wss.connect" => Ok(Box::new(true)), "network.protocol.wss.listen" => Ok(Box::new(false)), - "network.protocol.wss.max_connections" => Ok(Box::new(16u32)), + "network.protocol.wss.max_connections" => Ok(Box::new(32u32)), "network.protocol.wss.listen_address" => Ok(Box::new("".to_owned())), "network.protocol.wss.path" => Ok(Box::new(String::from("ws"))), "network.protocol.wss.url" => Ok(Box::new(Option::::None)), @@ -316,15 +316,15 @@ pub async fn test_config() { ); assert_eq!(inner.network.connection_initial_timeout_ms, 2_000u32); assert_eq!(inner.network.connection_inactivity_timeout_ms, 60_000u32); - assert_eq!(inner.network.max_connections_per_ip4, 8u32); - assert_eq!(inner.network.max_connections_per_ip6_prefix, 8u32); + assert_eq!(inner.network.max_connections_per_ip4, 32u32); + assert_eq!(inner.network.max_connections_per_ip6_prefix, 32u32); assert_eq!(inner.network.max_connections_per_ip6_prefix_size, 56u32); - assert_eq!(inner.network.max_connection_frequency_per_min, 8u32); + assert_eq!(inner.network.max_connection_frequency_per_min, 128u32); assert_eq!(inner.network.client_whitelist_timeout_ms, 300_000u32); assert_eq!(inner.network.reverse_connection_receipt_time_ms, 5_000u32); assert_eq!(inner.network.hole_punch_receipt_time_ms, 5_000u32); assert_eq!(inner.network.network_key_password, Option::::None); - assert_eq!(inner.network.rpc.concurrency, 2u32); + assert_eq!(inner.network.rpc.concurrency, 0u32); assert_eq!(inner.network.rpc.queue_size, 1024u32); assert_eq!(inner.network.rpc.timeout_ms, 5_000u32); assert_eq!(inner.network.rpc.max_route_hop_count, 4u8); @@ -366,7 +366,7 @@ pub async fn test_config() { assert!(!inner.network.upnp); assert!(inner.network.detect_address_changes); - assert_eq!(inner.network.restricted_nat_retries, 3u32); + assert_eq!(inner.network.restricted_nat_retries, 0u32); assert_eq!(inner.network.tls.certificate_path, get_certfile_path()); assert_eq!(inner.network.tls.private_key_path, get_keyfile_path()); assert_eq!(inner.network.tls.connection_initial_timeout_ms, 2_000u32); @@ -379,6 +379,7 @@ pub async fn test_config() { assert_eq!(inner.network.application.http.listen_address, ""); assert_eq!(inner.network.application.http.path, "app"); assert_eq!(inner.network.application.http.url, None); + assert!(inner.network.protocol.udp.enabled); assert_eq!(inner.network.protocol.udp.socket_pool_size, 16u32); assert_eq!(inner.network.protocol.udp.listen_address, ""); @@ -388,15 +389,15 @@ pub async fn test_config() { assert_eq!(inner.network.protocol.tcp.max_connections, 32u32); assert_eq!(inner.network.protocol.tcp.listen_address, ""); assert_eq!(inner.network.protocol.tcp.public_address, None); - assert!(!inner.network.protocol.ws.connect); - assert!(!inner.network.protocol.ws.listen); - assert_eq!(inner.network.protocol.ws.max_connections, 16u32); + assert!(inner.network.protocol.ws.connect); + assert!(inner.network.protocol.ws.listen); + assert_eq!(inner.network.protocol.ws.max_connections, 32u32); assert_eq!(inner.network.protocol.ws.listen_address, ""); assert_eq!(inner.network.protocol.ws.path, "ws"); assert_eq!(inner.network.protocol.ws.url, None); - assert!(!inner.network.protocol.wss.connect); + assert!(inner.network.protocol.wss.connect); assert!(!inner.network.protocol.wss.listen); - assert_eq!(inner.network.protocol.wss.max_connections, 16u32); + assert_eq!(inner.network.protocol.wss.max_connections, 32u32); assert_eq!(inner.network.protocol.wss.listen_address, ""); assert_eq!(inner.network.protocol.wss.path, "ws"); assert_eq!(inner.network.protocol.wss.url, None); diff --git a/veilid-core/tests/web.rs b/veilid-core/tests/web.rs index fdf33d0b..ea6daa49 100644 --- a/veilid-core/tests/web.rs +++ b/veilid-core/tests/web.rs @@ -2,9 +2,9 @@ #![cfg(target_arch = "wasm32")] #![recursion_limit = "256"] -use cfg_if::*; use parking_lot::Once; use serial_test::serial; +use tracing::*; use veilid_core::tests::*; use wasm_bindgen_test::*; @@ -18,17 +18,12 @@ static SETUP_ONCE: Once = Once::new(); pub fn setup() -> () { SETUP_ONCE.call_once(|| { console_error_panic_hook::set_once(); - cfg_if! { - if #[cfg(feature = "tracing")] { - let mut builder = tracing_wasm::WASMLayerConfigBuilder::new(); - builder.set_report_logs_in_timings(false); - builder.set_max_level(Level::TRACE); - builder.set_console_config(tracing_wasm::ConsoleConfig::ReportWithConsoleColor); - tracing_wasm::set_as_global_default_with_config(builder.build()); - } else { - wasm_logger::init(wasm_logger::Config::default()); - } - } + + let mut builder = tracing_wasm::WASMLayerConfigBuilder::new(); + builder.set_report_logs_in_timings(false); + builder.set_max_level(Level::DEBUG); + builder.set_console_config(tracing_wasm::ConsoleConfig::ReportWithoutConsoleColor); + tracing_wasm::set_as_global_default_with_config(builder.build()); }); } diff --git a/veilid-tools/src/tools.rs b/veilid-tools/src/tools.rs index 2ff88f67..1d8017c7 100644 --- a/veilid-tools/src/tools.rs +++ b/veilid-tools/src/tools.rs @@ -87,15 +87,7 @@ pub fn system_boxed<'a, Out>( ////////////////////////////////////////////////////////////////////////////////////////////////////////////// cfg_if! { - if #[cfg(target_arch = "wasm32")] { - - // xxx: for now until wasm threads are more stable, and/or we bother with web workers - pub fn get_concurrency() -> u32 { - 1 - } - - } else { - + if #[cfg(not(target_arch = "wasm32"))] { pub fn get_concurrency() -> u32 { std::thread::available_parallelism() .map(|x| x.get()) @@ -104,7 +96,6 @@ cfg_if! { 1 }) as u32 } - } } diff --git a/veilid-tools/src/wasm.rs b/veilid-tools/src/wasm.rs index 1d198fb3..66c33f5a 100644 --- a/veilid-tools/src/wasm.rs +++ b/veilid-tools/src/wasm.rs @@ -1,7 +1,7 @@ #![cfg(target_arch = "wasm32")] use super::*; -use core::sync::atomic::{AtomicI8, Ordering}; +use core::sync::atomic::{AtomicI8, AtomicU32, Ordering}; use js_sys::{global, Reflect}; use wasm_bindgen::prelude::*; @@ -82,3 +82,19 @@ pub fn is_ipv6_supported() -> bool { *opt_supp = Some(supp); supp } + +pub fn get_concurrency() -> u32 { + static CACHE: AtomicU32 = AtomicU32::new(0); + let cache = CACHE.load(Ordering::Acquire); + if cache != 0 { + return cache; + } + + let res = js_sys::eval("navigator.hardwareConcurrency") + .map(|res| res.as_f64().unwrap_or(1.0f64) as u32) + .unwrap_or(1); + + CACHE.store(res, Ordering::Release); + + res +} diff --git a/veilid-tools/tests/web.rs b/veilid-tools/tests/web.rs index af72b762..fd0fe7cc 100644 --- a/veilid-tools/tests/web.rs +++ b/veilid-tools/tests/web.rs @@ -22,7 +22,7 @@ pub fn setup() -> () { let mut builder = tracing_wasm::WASMLayerConfigBuilder::new(); builder.set_report_logs_in_timings(false); builder.set_max_level(Level::TRACE); - builder.set_console_config(tracing_wasm::ConsoleConfig::ReportWithConsoleColor); + builder.set_console_config(tracing_wasm::ConsoleConfig::ReportWithoutConsoleColor); tracing_wasm::set_as_global_default_with_config(builder.build()); } else { wasm_logger::init(wasm_logger::Config::default());