diff --git a/bitreq/src/client.rs b/bitreq/src/client.rs index b5de6f2fb..eff0b080f 100644 --- a/bitreq/src/client.rs +++ b/bitreq/src/client.rs @@ -66,7 +66,7 @@ impl Client { // Try to get cached connection let conn_opt = { - let state = self.r#async.lock().unwrap(); + let state = lock!(self.r#async); if let Some(conn) = state.connections.get(&owned_key) { Some(Arc::clone(conn)) @@ -80,7 +80,7 @@ impl Client { let connection = AsyncConnection::new(key, parsed_request.timeout_at).await?; let connection = Arc::new(connection); - let mut state = self.r#async.lock().unwrap(); + let mut state = lock!(self.r#async); if let hash_map::Entry::Vacant(entry) = state.connections.entry(owned_key) { entry.insert(Arc::clone(&connection)); state.lru_order.push_back(key.into()); diff --git a/bitreq/src/connection.rs b/bitreq/src/connection.rs index 44ffe03ac..4fd847429 100644 --- a/bitreq/src/connection.rs +++ b/bitreq/src/connection.rs @@ -368,7 +368,7 @@ impl AsyncConnection { request: ParsedRequest, ) -> Pin> + Send + 'a>> { Box::pin(async move { - let conn = Arc::clone(&*self.0.lock().unwrap()); + let conn = Arc::clone(&*lock!(self.0)); #[cfg(debug_assertions)] { let next_read = conn.readable_request_id.load(Ordering::Acquire); @@ -427,7 +427,7 @@ impl AsyncConnection { let new_connection = AsyncConnection::new(request.connection_params(), request.timeout_at) .await?; - *self.0.lock().unwrap() = Arc::clone(&*new_connection.0.lock().unwrap()); + *lock!(self.0) = Arc::clone(&*lock!(new_connection.0)); core::mem::drop(read); // Note that this cannot recurse infinitely as we'll always be able to send at // least one request on the new socket (though some other request may race us @@ -444,7 +444,7 @@ impl AsyncConnection { Self::timeout(request.timeout_at, conn.write.lock()).await? }; - let socket_timeout = *conn.socket_new_requests_timeout.lock().unwrap(); + let socket_timeout = *lock!(conn.socket_new_requests_timeout); let socket_timed_out = Instant::now() > socket_timeout; request_id = conn.next_request_id.fetch_add(1, Ordering::Relaxed); @@ -545,7 +545,7 @@ impl AsyncConnection { match k.trim() { "timeout" => { let timeout_secs = (v as u64).saturating_sub(1); - *conn.socket_new_requests_timeout.lock().unwrap() = + *lock!(conn.socket_new_requests_timeout) = Instant::now() .checked_add(Duration::from_secs(timeout_secs)) .unwrap_or(Instant::now()); diff --git a/bitreq/src/lib.rs b/bitreq/src/lib.rs index b8a53bbd6..e3ebf89bc 100644 --- a/bitreq/src/lib.rs +++ b/bitreq/src/lib.rs @@ -249,6 +249,29 @@ extern crate alloc; +/// Acquires a mutex lock, recovering from poisoning. +/// +/// A mutex becomes poisoned when a thread panics while holding the lock. +/// This macro ignores poisoning and returns the guard anyway, which is +/// usually the right behavior since: +/// - The data may still be in a valid state +/// - Propagating panics across threads is rarely useful +/// +/// # Example +/// +/// ```ignore +/// use std::sync::Mutex; +/// +/// let mutex = Mutex::new(42); +/// let guard = lock!(mutex); +/// ``` +#[cfg(feature = "async")] +macro_rules! lock { + ($mutex:expr) => { + $mutex.lock().unwrap_or_else(|e| e.into_inner()) + }; +} + #[cfg(feature = "std")] mod client; #[cfg(feature = "std")]