diff --git a/integration-tests/Cargo.lock b/integration-tests/Cargo.lock index 48e535076..9b309668e 100644 --- a/integration-tests/Cargo.lock +++ b/integration-tests/Cargo.lock @@ -2669,7 +2669,7 @@ dependencies = [ [[package]] name = "integration_tests_sv2" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel 1.9.0", "clap", @@ -2747,7 +2747,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jd_client_sv2" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel 1.9.0", "clap", @@ -2762,11 +2762,10 @@ dependencies = [ [[package]] name = "jd_server_sv2" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel 1.9.0", "async-trait", - "dashmap", "hotpath", "serde", "stratum-apps", @@ -3430,7 +3429,7 @@ dependencies = [ [[package]] name = "pool_sv2" -version = "0.6.0" +version = "0.7.0" dependencies = [ "async-channel 1.9.0", "clap", @@ -4369,7 +4368,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratum-apps" -version = "0.7.0" +version = "0.8.0" dependencies = [ "asic-rs", "async-channel 1.9.0", @@ -4897,7 +4896,7 @@ dependencies = [ [[package]] name = "translator_sv2" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel 1.9.0", "clap", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index b9b143e76..bf1c67c20 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "integration_tests_sv2" -version = "0.5.0" +version = "0.6.0" authors = ["The Stratum V2 Developers"] edition = "2021" description = "Sv2 Integration Tests Framework" @@ -14,10 +14,10 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] exclude = ["resources/high_diff_chain.tar.gz"] [dependencies] -stratum-apps = { version = "0.7.0", path = "../stratum-apps", features = ["network", "config", "payout", "bitcoin-core-sv2"] } -jd_client_sv2 = { version = "0.5.0", path = "../miner-apps/jd-client" } -pool_sv2 = { version = "0.6.0", path = "../pool-apps/pool" } -translator_sv2 = { version = "0.5.0", path = "../miner-apps/translator" } +stratum-apps = { version = "0.8.0", path = "../stratum-apps", features = ["network", "config", "payout", "bitcoin-core-sv2"] } +jd_client_sv2 = { version = "0.6.0", path = "../miner-apps/jd-client" } +pool_sv2 = { version = "0.7.0", path = "../pool-apps/pool" } +translator_sv2 = { version = "0.6.0", path = "../miner-apps/translator" } async-channel = { version = "1.5.1", default-features = false } corepc-node = { version = "0.7.0", default-features = false, features = ["28_0"] } minreq = { version = "2.12.0", default-features = false, features = ["https"] } diff --git a/miner-apps/Cargo.lock b/miner-apps/Cargo.lock index b2c588e1d..62b39946e 100644 --- a/miner-apps/Cargo.lock +++ b/miner-apps/Cargo.lock @@ -2339,7 +2339,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jd_client_sv2" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel", "clap", @@ -3740,7 +3740,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratum-apps" -version = "0.7.0" +version = "0.8.0" dependencies = [ "asic-rs", "async-channel", @@ -4289,7 +4289,7 @@ dependencies = [ [[package]] name = "translator_sv2" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel", "clap", diff --git a/miner-apps/jd-client/Cargo.toml b/miner-apps/jd-client/Cargo.toml index 7abb82987..0c94f81ed 100644 --- a/miner-apps/jd-client/Cargo.toml +++ b/miner-apps/jd-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jd_client_sv2" -version = "0.5.0" +version = "0.6.0" authors = ["The Stratum V2 Developers"] edition = "2021" description = "Job Declarator Client (JDC) role" @@ -16,7 +16,7 @@ name = "jd_client_sv2" path = "src/lib/mod.rs" [dependencies] -stratum-apps = { version = "0.7.0", path = "../../stratum-apps", features = ["jd_client"] } +stratum-apps = { version = "0.8.0", path = "../../stratum-apps", features = ["jd_client"] } async-channel = "1.5.1" serde = { version = "1.0.89", default-features = false, features = ["derive", "alloc"] } tokio = { version = "1.44.1", features = ["full"] } diff --git a/miner-apps/translator/Cargo.toml b/miner-apps/translator/Cargo.toml index 14c61a22c..56dc48beb 100644 --- a/miner-apps/translator/Cargo.toml +++ b/miner-apps/translator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "translator_sv2" -version = "0.5.0" +version = "0.6.0" authors = ["The Stratum V2 Developers"] edition = "2021" description = "SV1 to SV2 translation proxy" @@ -20,7 +20,7 @@ name = "translator_sv2" path = "src/main.rs" [dependencies] -stratum-apps = { version = "0.7.0", path = "../../stratum-apps", features = ["translator"] } +stratum-apps = { version = "0.8.0", path = "../../stratum-apps", features = ["translator"] } async-channel = "1.5.1" serde = { version = "1.0.89", default-features = false, features = ["derive", "alloc"] } serde_json = { version = "1.0.64", default-features = false, features = ["alloc"] } diff --git a/pool-apps/Cargo.lock b/pool-apps/Cargo.lock index e854c89ac..f915ac19d 100644 --- a/pool-apps/Cargo.lock +++ b/pool-apps/Cargo.lock @@ -1519,11 +1519,10 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jd_server_sv2" -version = "0.5.0" +version = "0.6.0" dependencies = [ "async-channel", "async-trait", - "dashmap", "hotpath", "serde", "stratum-apps", @@ -1921,7 +1920,7 @@ dependencies = [ [[package]] name = "pool_sv2" -version = "0.6.0" +version = "0.7.0" dependencies = [ "async-channel", "clap", @@ -2448,7 +2447,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratum-apps" -version = "0.7.0" +version = "0.8.0" dependencies = [ "async-channel", "axum", diff --git a/pool-apps/jd-server/Cargo.toml b/pool-apps/jd-server/Cargo.toml index 152261c28..3936023b6 100644 --- a/pool-apps/jd-server/Cargo.toml +++ b/pool-apps/jd-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jd_server_sv2" -version = "0.5.0" +version = "0.6.0" authors = ["The Stratum V2 Developers"] edition = "2021" description = "Sv2 Job Declaration Server" @@ -16,12 +16,11 @@ name = "jd_server_sv2" path = "src/lib/mod.rs" [dependencies] -stratum-apps = { version = "0.7.0", path = "../../stratum-apps", features = ["jd_server"] } +stratum-apps = { version = "0.8.0", path = "../../stratum-apps", features = ["jd_server"] } async-channel = "1.5.1" serde = { version = "1.0.89", features = ["derive", "alloc"], default-features = false } tracing = { version = "0.1" } tokio = { version = "1.44.1", features = ["full"] } -dashmap = "6.1.0" async-trait = "0.1" hotpath = "0.14.0" diff --git a/pool-apps/jd-server/src/lib/error.rs b/pool-apps/jd-server/src/lib/error.rs index b56479377..b8aa313c7 100644 --- a/pool-apps/jd-server/src/lib/error.rs +++ b/pool-apps/jd-server/src/lib/error.rs @@ -12,7 +12,7 @@ //! The `Owner` type parameter is a zero-sized marker (e.g. [`JobDeclarator`], [`Downstream`]) //! that controls which constructors (`shutdown`, `disconnect`) are available at the type level. -use std::{fmt::Debug, marker::PhantomData}; +use std::{fmt::Debug, marker::PhantomData, sync::PoisonError}; use stratum_apps::{ stratum_core::{ @@ -119,6 +119,13 @@ pub enum JDSErrorKind { UnsupportedConnectionFlags, OneshotRecv(tokio::sync::oneshot::error::RecvError), InvalidConfig(String), + PoisonLock, +} + +impl From> for JDSErrorKind { + fn from(_: PoisonError) -> Self { + JDSErrorKind::PoisonLock + } } impl From> for JDSErrorKind { diff --git a/pool-apps/jd-server/src/lib/job_declarator/downstream/common_message_handler.rs b/pool-apps/jd-server/src/lib/job_declarator/downstream/common_message_handler.rs index 73d0365ad..612580896 100644 --- a/pool-apps/jd-server/src/lib/job_declarator/downstream/common_message_handler.rs +++ b/pool-apps/jd-server/src/lib/job_declarator/downstream/common_message_handler.rs @@ -25,9 +25,7 @@ impl HandleCommonMessagesFromClientAsync for Downstream { &self, _client_id: Option, ) -> Result, Self::Error> { - Ok(self - .negotiated_extensions - .super_safe_lock(|extensions| extensions.clone())) + self.negotiated_extensions.get().map_err(JDSError::shutdown) } async fn handle_setup_connection( diff --git a/pool-apps/jd-server/src/lib/job_declarator/downstream/mod.rs b/pool-apps/jd-server/src/lib/job_declarator/downstream/mod.rs index 40cca2346..cae13383f 100644 --- a/pool-apps/jd-server/src/lib/job_declarator/downstream/mod.rs +++ b/pool-apps/jd-server/src/lib/job_declarator/downstream/mod.rs @@ -14,14 +14,12 @@ use crate::{ io_task::spawn_io_tasks, }; use async_channel::{unbounded, Receiver, Sender}; -use dashmap::DashMap; use std::{ sync::Arc, time::{Duration, Instant}, }; use stratum_apps::{ bitcoin_core_sv2::CancellationToken, - custom_mutex::Mutex, network_helpers::noise_stream::NoiseTcpStream, stratum_core::{ common_messages_sv2::MESSAGE_TYPE_SETUP_CONNECTION, @@ -30,6 +28,7 @@ use stratum_apps::{ job_declaration_sv2::DeclareMiningJob, parsers_sv2::{parse_message_frame_with_tlvs, AnyMessage}, }, + sync::{SharedLock, SharedMap}, task_manager::TaskManager, utils::{ protocol_message_type::{protocol_message_type, MessageType}, @@ -58,9 +57,9 @@ pub struct DownstreamIo { #[derive(Clone)] pub struct Downstream { /// Extensions that have been successfully negotiated with this client. - pub negotiated_extensions: Arc>>, + pub negotiated_extensions: SharedLock>, /// Jobs waiting for missing transactions (keyed by `request_id`). - pub pending_declare_mining_jobs: Arc>, + pub pending_declare_mining_jobs: SharedMap, pub downstream_io: DownstreamIo, pub downstream_id: DownstreamId, /// Extensions that JDS supports @@ -137,8 +136,8 @@ impl Downstream { downstream_cancellation_token.clone(), ); - let negotiated_extensions = Arc::new(Mutex::new(Vec::new())); - let pending_declare_mining_jobs = Arc::new(DashMap::new()); + let negotiated_extensions = SharedLock::new(Vec::new()); + let pending_declare_mining_jobs = SharedMap::new(); let downstream_io = DownstreamIo { to_job_declarator_sender, @@ -242,7 +241,7 @@ impl Downstream { /// older than `ALLOCATED_TOKEN_TIMEOUT_SECS`. fn spawn_pending_jobs_janitor(&self, task_manager: Arc) { let cancellation_token = self.downstream_cancellation_token.clone(); - let pending_declare_mining_jobs = Arc::clone(&self.pending_declare_mining_jobs); + let pending_declare_mining_jobs = self.pending_declare_mining_jobs.clone(); let downstream_id = self.downstream_id; let token_timeout = Duration::from_secs(ALLOCATED_TOKEN_TIMEOUT_SECS); let janitor_interval = Duration::from_secs(JANITOR_INTERVAL_SECS); @@ -351,7 +350,8 @@ impl Downstream { debug!("Received mining SV2 frame from downstream."); let negotiated_extensions = self .negotiated_extensions - .super_safe_lock(|extensions| extensions.clone()); + .get() + .map_err(error::JDSError::shutdown)?; let (any_message, tlv_fields) = parse_message_frame_with_tlvs( header, sv2_frame.payload(), diff --git a/pool-apps/jd-server/src/lib/job_declarator/job_declaration_message_handler.rs b/pool-apps/jd-server/src/lib/job_declarator/job_declaration_message_handler.rs index f09f565fd..0115b8cda 100644 --- a/pool-apps/jd-server/src/lib/job_declarator/job_declaration_message_handler.rs +++ b/pool-apps/jd-server/src/lib/job_declarator/job_declaration_message_handler.rs @@ -33,12 +33,18 @@ impl HandleJobDeclarationMessagesFromClientAsync for JobDeclarator { let client_id = client_id.ok_or_else(|| JDSError::shutdown(error::JDSErrorKind::ClientNotFound(0)))?; // Disconnect: downstream may have been cleaned up between message dispatch and handling. - let negotiated_extensions = self.downstream_clients.get(&client_id).ok_or_else(|| { - JDSError::disconnect(error::JDSErrorKind::ClientNotFound(client_id), client_id) - })?; - Ok(negotiated_extensions - .negotiated_extensions - .super_safe_lock(|extensions| extensions.clone())) + let Some(negotiated_extensions) = self.downstream_clients.with(&client_id, |downstream| { + downstream + .negotiated_extensions + .get() + .map_err(JDSError::shutdown) + }) else { + return Err(JDSError::disconnect( + error::JDSErrorKind::ClientNotFound(client_id), + client_id, + )); + }; + negotiated_extensions } async fn handle_allocate_mining_job_token( @@ -79,14 +85,13 @@ impl HandleJobDeclarationMessagesFromClientAsync for JobDeclarator { let client_sender = self .job_declarator_io .downstream_client_senders - .get(&client_id) + .with(&client_id, |sender| sender.clone()) .ok_or_else(|| { error::JDSError::disconnect( error::JDSErrorKind::ClientSenderNotFound(client_id), client_id, ) - })? - .clone(); + })?; client_sender .send(( JobDeclaration::AllocateMiningJobTokenSuccess(allocate_mining_job_token_success), @@ -112,14 +117,13 @@ impl HandleJobDeclarationMessagesFromClientAsync for JobDeclarator { let client_sender = self .job_declarator_io .downstream_client_senders - .get(&client_id) + .with(&client_id, |sender| sender.clone()) .ok_or_else(|| { error::JDSError::disconnect( error::JDSErrorKind::ClientSenderNotFound(client_id), client_id, ) - })? - .clone(); + })?; // can we parse `DeclareMiningJob.mining_job_token` into a `JdToken`? let token: JdToken = match msg.mining_job_token.try_as_array::<8>() { @@ -133,7 +137,7 @@ impl HandleJobDeclarationMessagesFromClientAsync for JobDeclarator { .to_vec() .try_into() .expect("error code must be valid B0_255"), - error_details: Vec::new().try_into().unwrap(), + error_details: Vec::new().try_into().map_err(error::JDSError::shutdown)?, }; client_sender @@ -155,7 +159,7 @@ impl HandleJobDeclarationMessagesFromClientAsync for JobDeclarator { .to_vec() .try_into() .expect("error code must be valid B0_255"), - error_details: Vec::new().try_into().unwrap(), + error_details: Vec::new().try_into().map_err(error::JDSError::shutdown)?, }; client_sender .send((JobDeclaration::DeclareMiningJobError(error_message), None)) @@ -223,12 +227,18 @@ impl HandleJobDeclarationMessagesFromClientAsync for JobDeclarator { // ProvideMissingTransactions DeclareMiningJobResult::MissingTransactions(missing_wtxids) => { // Disconnect: downstream may have disconnected between dispatch and handling. - let downstream = self.downstream_clients.get(&client_id).ok_or_else(|| { - JDSError::disconnect(error::JDSErrorKind::ClientNotFound(client_id), client_id) - })?; - downstream - .pending_declare_mining_jobs - .insert(msg.request_id, (Instant::now(), msg.as_static())); + self.downstream_clients + .with(&client_id, |downstream| { + downstream + .pending_declare_mining_jobs + .insert(msg.request_id, (Instant::now(), msg.as_static())); + }) + .ok_or_else(|| { + JDSError::disconnect( + error::JDSErrorKind::ClientNotFound(client_id), + client_id, + ) + })?; // Convert missing Wtxids to u16 indices by finding their positions in // DeclareMiningJob.wtxid_list @@ -277,25 +287,25 @@ impl HandleJobDeclarationMessagesFromClientAsync for JobDeclarator { let client_sender = self .job_declarator_io .downstream_client_senders - .get(&client_id) + .with(&client_id, |sender| sender.clone()) .ok_or_else(|| { error::JDSError::disconnect( error::JDSErrorKind::ClientSenderNotFound(client_id), client_id, ) - })? - .clone(); + })?; - // Scope downstream guard so it's dropped before later awaits. - let maybe_pending_declare_mining_job = { - // Disconnect: downstream may have disconnected between dispatch and handling. - let downstream = self.downstream_clients.get(&client_id).ok_or_else(|| { + // Disconnect: downstream may have disconnected between dispatch and handling. + let maybe_pending_declare_mining_job = self + .downstream_clients + .with(&client_id, |downstream| { + downstream + .pending_declare_mining_jobs + .remove(&msg.request_id) + }) + .ok_or_else(|| { JDSError::disconnect(error::JDSErrorKind::ClientNotFound(client_id), client_id) })?; - downstream - .pending_declare_mining_jobs - .remove(&msg.request_id) - }; let pending_declare_mining_job = match maybe_pending_declare_mining_job { Some((_, (_, declare_mining_job))) => declare_mining_job, diff --git a/pool-apps/jd-server/src/lib/job_declarator/job_validation/bitcoin_core_ipc.rs b/pool-apps/jd-server/src/lib/job_declarator/job_validation/bitcoin_core_ipc.rs index 6aaa09df9..d8e089250 100644 --- a/pool-apps/jd-server/src/lib/job_declarator/job_validation/bitcoin_core_ipc.rs +++ b/pool-apps/jd-server/src/lib/job_declarator/job_validation/bitcoin_core_ipc.rs @@ -10,7 +10,6 @@ use crate::{ use std::{ collections::HashMap, path::PathBuf, - sync::{Arc, Mutex}, thread::JoinHandle, time::{Duration, Instant}, }; @@ -54,7 +53,7 @@ use stratum_apps::{ ERROR_CODE_SET_CUSTOM_MINING_JOB_STALE_CHAIN_TIP, }, }, - sync::SharedMap, + sync::{SharedLock, SharedMap}, tp_type::BitcoinNetwork, utils::types::{DownstreamId, JdToken, RequestId}, }; @@ -284,7 +283,7 @@ pub struct BitcoinCoreIPCEngine { request_sender: async_channel::Sender, downstream_states: SharedMap, cancellation_token: CancellationToken, - jdp_thread_handle: Arc>>>, + jdp_thread_handle: SharedLock>>, } #[cfg_attr(not(test), hotpath::measure_all)] @@ -451,7 +450,7 @@ impl BitcoinCoreIPCEngine { request_sender, downstream_states, cancellation_token, - jdp_thread_handle: Arc::new(Mutex::new(Some(jdp_thread_handle))), + jdp_thread_handle: SharedLock::new(Some(jdp_thread_handle)), }) } } @@ -470,13 +469,19 @@ fn validation_context_drifted( impl JobValidationEngine for BitcoinCoreIPCEngine { fn shutdown(&self) { self.cancellation_token.cancel(); - if let Ok(mut handle_guard) = self.jdp_thread_handle.lock() { - if let Some(handle) = handle_guard.take() { - if let Err(e) = handle.join() { - tracing::warn!("BitcoinCoreSv2JDP thread join failed during shutdown: {e:?}"); + self.jdp_thread_handle + .with(|handle| { + if let Some(handle) = handle.take() { + if let Err(e) = handle.join() { + tracing::warn!( + "BitcoinCoreSv2JDP thread join failed during shutdown: {e:?}" + ); + } } - } - } + }) + .unwrap_or_else(|_| { + tracing::warn!("BitcoinCoreSv2JDP thread handle lock poisoned during shutdown"); + }); } fn cleanup_downstream(&self, downstream_id: DownstreamId) { diff --git a/pool-apps/jd-server/src/lib/job_declarator/mod.rs b/pool-apps/jd-server/src/lib/job_declarator/mod.rs index 6a73424c2..b7c0c866f 100644 --- a/pool-apps/jd-server/src/lib/job_declarator/mod.rs +++ b/pool-apps/jd-server/src/lib/job_declarator/mod.rs @@ -15,7 +15,6 @@ use crate::{ }, }; use async_channel::{unbounded, Receiver, Sender}; -use dashmap::DashMap; use std::{ net::SocketAddr, sync::{ @@ -36,6 +35,7 @@ use stratum_apps::{ }, parsers_sv2::{JobDeclaration, Tlv}, }, + sync::SharedMap, task_manager::TaskManager, utils::types::{DownstreamId, JdToken}, }; @@ -98,7 +98,7 @@ impl SetCustomMiningJobResponse<'_> { /// - `disconnect_sender/receiver`: channel through which downstreams signal disconnection. #[derive(Clone)] pub struct JobDeclaratorIo { - downstream_client_senders: DashMap>, + downstream_client_senders: SharedMap>, job_declarator_sender: Sender, job_declarator_receiver: Receiver, } @@ -113,7 +113,7 @@ pub struct JobDeclarator { job_validator: Arc, job_declarator_io: Arc, coinbase_reward_script: CoinbaseRewardScript, - downstream_clients: Arc>, + downstream_clients: SharedMap, downstream_id_factory: Arc, } @@ -131,7 +131,7 @@ impl JobDeclarator { let job_declarator_io = Arc::new(JobDeclaratorIo { job_declarator_sender, job_declarator_receiver, - downstream_client_senders: DashMap::new(), + downstream_client_senders: SharedMap::new(), }); let token_manager = @@ -142,7 +142,7 @@ impl JobDeclarator { job_validator: engine, job_declarator_io, coinbase_reward_script, - downstream_clients: Arc::new(DashMap::new()), + downstream_clients: SharedMap::new(), downstream_id_factory: Arc::new(AtomicUsize::new(0)), }) } diff --git a/pool-apps/jd-server/src/lib/job_declarator/token_management/mod.rs b/pool-apps/jd-server/src/lib/job_declarator/token_management/mod.rs index 457467721..8cb74b8f9 100644 --- a/pool-apps/jd-server/src/lib/job_declarator/token_management/mod.rs +++ b/pool-apps/jd-server/src/lib/job_declarator/token_management/mod.rs @@ -19,7 +19,6 @@ //! - Checking if a token is active. use super::{ACTIVE_TOKEN_TIMEOUT_SECS, ALLOCATED_TOKEN_TIMEOUT_SECS, JANITOR_INTERVAL_SECS}; -use dashmap::DashMap; use std::{ sync::{ atomic::{AtomicU64, Ordering}, @@ -29,6 +28,7 @@ use std::{ }; use stratum_apps::{ bitcoin_core_sv2::CancellationToken, + sync::SharedMap, task_manager::TaskManager, utils::types::{DownstreamId, JdToken}, }; @@ -48,8 +48,8 @@ pub type ActiveTokenData = (JdToken, Instant, DownstreamId); #[derive(Clone)] pub struct TokenManager { token_factory: Arc, - allocated_tokens: Arc>, - active_tokens: Arc>, + allocated_tokens: SharedMap, + active_tokens: SharedMap, cancellation_token: CancellationToken, task_manager: Arc, } @@ -63,8 +63,8 @@ impl TokenManager { pub fn new(cancellation_token: CancellationToken, task_manager: Arc) -> Self { let token_manager = Self { token_factory: Arc::new(AtomicU64::new(0)), - allocated_tokens: Arc::new(DashMap::new()), - active_tokens: Arc::new(DashMap::new()), + allocated_tokens: SharedMap::new(), + active_tokens: SharedMap::new(), cancellation_token, task_manager, }; @@ -87,11 +87,9 @@ impl TokenManager { /// Checks if a token is allocated. pub fn is_allocated(&self, token: JdToken, downstream_id: DownstreamId) -> bool { - if let Some(allocation_info) = self.allocated_tokens.get(&token) { - allocation_info.1 == downstream_id - } else { - false - } + self.allocated_tokens + .with(&token, |(_, owner)| *owner == downstream_id) + .unwrap_or(false) } /// Takes an allocated token and removes it from the internal set. @@ -148,8 +146,9 @@ impl TokenManager { pub fn allocated_from_active(&self, active_token: JdToken) -> Option<(JdToken, DownstreamId)> { let mapped = self .active_tokens - .get(&active_token) - .map(|entry| (entry.0, entry.2)); + .with(&active_token, |(allocated, _, downstream_id)| { + (*allocated, *downstream_id) + }); debug!( active_token, mapped_allocated_token = mapped.map(|(allocated, _)| allocated), @@ -199,8 +198,8 @@ impl TokenManager { /// Spawns a janitor task that removes expired allocated and active tokens. fn spawn_janitor_task(&self) { let cancellation_token = self.cancellation_token.clone(); - let allocated_tokens = Arc::clone(&self.allocated_tokens); - let active_tokens = Arc::clone(&self.active_tokens); + let allocated_tokens = self.allocated_tokens.clone(); + let active_tokens = self.active_tokens.clone(); let allocated_token_timeout = Duration::from_secs(ALLOCATED_TOKEN_TIMEOUT_SECS); let active_token_timeout = Duration::from_secs(ACTIVE_TOKEN_TIMEOUT_SECS); let janitor_interval = Duration::from_secs(JANITOR_INTERVAL_SECS); @@ -211,7 +210,7 @@ impl TokenManager { break; } _ = tokio::time::sleep(janitor_interval) => { - // Avoid removing while iterating the same DashMap, which can block. + // Avoid removing while iterating the same map, which can block. let now = Instant::now(); let allocated_before = allocated_tokens.len(); diff --git a/pool-apps/pool/Cargo.toml b/pool-apps/pool/Cargo.toml index 195e382a1..aec41210a 100644 --- a/pool-apps/pool/Cargo.toml +++ b/pool-apps/pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pool_sv2" -version = "0.6.0" +version = "0.7.0" authors = ["The Stratum V2 Developers"] edition = "2021" description = "SV2 pool role" @@ -17,14 +17,14 @@ name = "pool_sv2" path = "src/lib/mod.rs" [dependencies] -stratum-apps = { version = "0.7.0", path = "../../stratum-apps", features = ["pool"] } +stratum-apps = { version = "0.8.0", path = "../../stratum-apps", features = ["pool"] } async-channel = "1.5.1" serde = { version = "1.0.89", features = ["derive", "alloc"], default-features = false } tokio = { version = "1.44.1", features = ["full"] } ext-config = { version = "0.14.0", features = ["toml"], package = "config" } tracing = { version = "0.1" } clap = { version = "4.5.39", features = ["derive"] } -jd_server_sv2 = { version = "0.5.0", path = "../jd-server" } +jd_server_sv2 = { version = "0.6.0", path = "../jd-server" } hex = "0.4.3" hotpath = "0.14.0" diff --git a/stratum-apps/Cargo.lock b/stratum-apps/Cargo.lock index 20a3e788a..53f4499ef 100644 --- a/stratum-apps/Cargo.lock +++ b/stratum-apps/Cargo.lock @@ -3408,7 +3408,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratum-apps" -version = "0.7.0" +version = "0.8.0" dependencies = [ "asic-rs", "async-channel", diff --git a/stratum-apps/Cargo.toml b/stratum-apps/Cargo.toml index 4fe348a50..816c7ca63 100644 --- a/stratum-apps/Cargo.toml +++ b/stratum-apps/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stratum-apps" -version = "0.7.0" +version = "0.8.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" diff --git a/stratum-apps/src/sync.rs b/stratum-apps/src/sync.rs index 0c50dc7cd..eeb39d6ad 100644 --- a/stratum-apps/src/sync.rs +++ b/stratum-apps/src/sync.rs @@ -246,6 +246,17 @@ impl SharedMap { self.0.remove(key) } + /// Remove a key only when the predicate matches the current entry. + /// + /// Caution: `f` runs while `DashMap` is mutating internal shards. Avoid + /// re-entering this `SharedMap` from inside the predicate. + pub fn remove_if(&self, key: &K, f: F) -> Option<(K, V)> + where + F: FnOnce(&K, &V) -> bool, + { + self.0.remove_if(key, f) + } + /// Check if a key exists. pub fn contains_key(&self, key: &K) -> bool { self.0.contains_key(key) @@ -343,6 +354,9 @@ impl SharedSet { } /// Remove a key if the predicate returns `true`, returning it if present. + /// + /// Caution: `f` runs while `DashSet` is mutating internal shards. Avoid + /// re-entering this `SharedSet` from inside the predicate. pub fn remove_if(&self, key: &K, f: F) -> Option where F: FnOnce(&K) -> bool,