Skip to content

Use std sync primitives instead of parking_lot #343

@BratSinot

Description

@BratSinot

Greetings!

Since 1.62 rust start to use sync primitives based on futex (unstead of pthreads) in */Linux systems and it become equal (or faster in some cases) by speed with parking_lot.

We can save "poison" behaviour from parking_lot by this:

    use std::sync::{PoisonError, RwLockReadGuard, RwLockWriteGuard};

    pub(crate) struct RwLock<T>(std::sync::RwLock<T>);

    impl<T> RwLock<T> {
        #[inline]
        pub(crate) fn new(t: T) -> Self {
            Self(std::sync::RwLock::new(t))
        }

        #[inline]
        pub(crate) fn read(&self) -> RwLockReadGuard<T> {
            self.0.read().unwrap_or_else(PoisonError::into_inner)
        }

        #[inline]
        pub(crate) fn write(&self) -> RwLockWriteGuard<T> {
            self.0.write().unwrap_or_else(PoisonError::into_inner)
        }
    }

But the problem is that minimal rust version in yours crates is 1.56.1 and if someone use older compiler version speed will degradate.
In some crates I can add parking_lot as optional:

diff --git a/metrics-exporter-prometheus/Cargo.toml b/metrics-exporter-prometheus/Cargo.toml
index e24aaf2..e6160ea 100644
--- a/metrics-exporter-prometheus/Cargo.toml
+++ b/metrics-exporter-prometheus/Cargo.toml
@@ -17,7 +17,7 @@ categories = ["development-tools::debugging"]
 keywords = ["metrics", "telemetry", "prometheus"]
 
 [features]
-default = ["http-listener", "push-gateway"]
+default = ["http-listener", "push-gateway", "parking_lot"]
 async-runtime = ["tokio", "hyper"]
 http-listener = ["async-runtime", "hyper/server", "ipnet"]
 push-gateway = ["async-runtime", "hyper/client", "tracing"]
@@ -25,7 +25,7 @@ push-gateway = ["async-runtime", "hyper/client", "tracing"]
 [dependencies]
 metrics = { version = "^0.20", path = "../metrics" }
 metrics-util = { version = "^0.14", path = "../metrics-util", default-features = false, features = ["recency", "registry", "summary"] }
-parking_lot = { version = "0.12", default-features = false }
+parking_lot = { version = "0.12", optional = true, default-features = false }
 thiserror = { version = "1", default-features = false }
 quanta = { version = "0.10.0", default-features = false }
 indexmap = { version = "1", default-features = false }
diff --git a/metrics-exporter-prometheus/src/builder.rs b/metrics-exporter-prometheus/src/builder.rs
index 2e5bd6c..2db688a 100644
--- a/metrics-exporter-prometheus/src/builder.rs
+++ b/metrics-exporter-prometheus/src/builder.rs
@@ -31,7 +31,6 @@ use hyper::{
 use indexmap::IndexMap;
 #[cfg(feature = "http-listener")]
 use ipnet::IpNet;
-use parking_lot::RwLock;
 use quanta::Clock;
 #[cfg(any(feature = "http-listener", feature = "push-gateway"))]
 use tokio::runtime;
@@ -48,6 +47,7 @@ use crate::common::Matcher;
 use crate::distribution::DistributionBuilder;
 use crate::recorder::{Inner, PrometheusRecorder};
 use crate::registry::AtomicStorage;
+use crate::sync::RwLock;
 use crate::{common::BuildError, PrometheusHandle};
 
 #[cfg(any(feature = "http-listener", feature = "push-gateway"))]
@@ -480,7 +480,7 @@ impl PrometheusBuilder {
                                     let body = body
                                         .map_err(|_| ())
                                         .map(|mut b| b.copy_to_bytes(b.remaining()))
-                                        .map(|b| (&b[..]).to_vec())
+                                        .map(|b| (b[..]).to_vec())
                                         .and_then(|s| String::from_utf8(s).map_err(|_| ()))
                                         .unwrap_or_else(|_| {
                                             String::from("<failed to read response body>")
diff --git a/metrics-exporter-prometheus/src/lib.rs b/metrics-exporter-prometheus/src/lib.rs
index 399d942..e19dcd9 100644
--- a/metrics-exporter-prometheus/src/lib.rs
+++ b/metrics-exporter-prometheus/src/lib.rs
@@ -113,5 +113,6 @@ pub mod formatting;
 mod recorder;
 
 mod registry;
+mod sync;
 
 pub use self::recorder::{PrometheusHandle, PrometheusRecorder};
diff --git a/metrics-exporter-prometheus/src/recorder.rs b/metrics-exporter-prometheus/src/recorder.rs
index 6d746df..223969b 100644
--- a/metrics-exporter-prometheus/src/recorder.rs
+++ b/metrics-exporter-prometheus/src/recorder.rs
@@ -5,7 +5,6 @@ use std::sync::Arc;
 use indexmap::IndexMap;
 use metrics::{Counter, Gauge, Histogram, Key, KeyName, Recorder, SharedString, Unit};
 use metrics_util::registry::{Recency, Registry};
-use parking_lot::RwLock;
 use quanta::Instant;
 
 use crate::common::Snapshot;
@@ -14,6 +13,7 @@ use crate::formatting::{
     key_to_parts, sanitize_metric_name, write_help_line, write_metric_line, write_type_line,
 };
 use crate::registry::GenerationalAtomicStorage;
+use crate::sync::RwLock;
 
 pub(crate) struct Inner {
     pub registry: Registry<Key, GenerationalAtomicStorage>,
diff --git a/metrics-exporter-prometheus/src/sync.rs b/metrics-exporter-prometheus/src/sync.rs
index e69de29..dfb3b8d 100644
--- a/metrics-exporter-prometheus/src/sync.rs
+++ b/metrics-exporter-prometheus/src/sync.rs
@@ -0,0 +1,29 @@
+#[cfg(feature = "parking_lot")]
+pub(crate) use parking_lot::RwLock;
+
+#[cfg(not(feature = "parking_lot"))]
+mod std_mutex {
+    use std::sync::{PoisonError, RwLockReadGuard, RwLockWriteGuard};
+
+    pub(crate) struct RwLock<T>(std::sync::RwLock<T>);
+
+    impl<T> RwLock<T> {
+        #[inline]
+        pub(crate) fn new(t: T) -> Self {
+            Self(std::sync::RwLock::new(t))
+        }
+
+        #[inline]
+        pub(crate) fn read(&self) -> RwLockReadGuard<T> {
+            self.0.read().unwrap_or_else(PoisonError::into_inner)
+        }
+
+        #[inline]
+        pub(crate) fn write(&self) -> RwLockWriteGuard<T> {
+            self.0.write().unwrap_or_else(PoisonError::into_inner)
+        }
+    }
+}
+
+#[cfg(not(feature = "parking_lot"))]
+pub(crate) use std_mutex::*;

But in some crates it already optional hidden by different flag, like in metrics-util:

recency = ["parking_lot", "registry", "quanta"]

and I can't make usage of std sync primitive by flag and save old behaviour.

I see only 3 ways:

  1. remove parking_lot deps;
  2. use std sync primitives by default and make usage of parking_lot optional;
  3. doesn't bore at all and continue to use parking_lot;

But I want to remove extra deps and option 3 isn't acceptable personally for me =)

Any thoughts on this case?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions