Skip to content

Commit

Permalink
Use read-write locks instead of mutexes in authentication handling (#…
Browse files Browse the repository at this point in the history
…3210)

- Use `RwLock` for `KeyringProvider` cache
- Use `RwLock` for `CredentialsCache`
  • Loading branch information
zanieb committed Apr 24, 2024
1 parent 0b84eb0 commit a07adf7
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 20 deletions.
18 changes: 9 additions & 9 deletions crates/uv-auth/src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::sync::Arc;
use std::{collections::HashMap, sync::Mutex};
use std::{collections::HashMap, sync::RwLock};

use crate::credentials::{Credentials, Username};
use crate::Realm;
Expand All @@ -10,11 +10,11 @@ use url::Url;

pub struct CredentialsCache {
/// A cache per realm and username
realms: Mutex<HashMap<(Realm, Username), Arc<Credentials>>>,
realms: RwLock<HashMap<(Realm, Username), Arc<Credentials>>>,
/// A cache tracking the result of fetches from external services
pub(crate) fetches: OnceMap<(Realm, Username), Option<Arc<Credentials>>>,
/// A cache per URL, uses a trie for efficient prefix queries.
urls: Mutex<UrlTrie>,
urls: RwLock<UrlTrie>,
}

impl Default for CredentialsCache {
Expand All @@ -27,15 +27,15 @@ impl CredentialsCache {
/// Create a new cache.
pub fn new() -> Self {
Self {
realms: Mutex::new(HashMap::new()),
fetches: OnceMap::default(),
urls: Mutex::new(UrlTrie::new()),
realms: RwLock::new(HashMap::new()),
urls: RwLock::new(UrlTrie::new()),
}
}

/// Return the credentials that should be used for a realm and username, if any.
pub(crate) fn get_realm(&self, realm: Realm, username: Username) -> Option<Arc<Credentials>> {
let realms = self.realms.lock().unwrap();
let realms = self.realms.read().unwrap();
let name = if let Some(username) = username.as_deref() {
format!("{username}@{realm}")
} else {
Expand Down Expand Up @@ -65,7 +65,7 @@ impl CredentialsCache {
/// cached credentials have a username equal to the provided one — otherwise `None` is returned.
/// If multiple usernames are used per URL, the realm cache should be queried instead.
pub(crate) fn get_url(&self, url: &Url, username: Username) -> Option<Arc<Credentials>> {
let urls = self.urls.lock().unwrap();
let urls = self.urls.read().unwrap();
let credentials = urls.get(url);
if let Some(credentials) = credentials {
if username.is_none() || username.as_deref() == credentials.username() {
Expand Down Expand Up @@ -100,7 +100,7 @@ impl CredentialsCache {
self.insert_realm((Realm::from(url), Username::none()), credentials.clone());

// Insert an entry for the URL
let mut urls = self.urls.lock().unwrap();
let mut urls = self.urls.write().unwrap();
urls.insert(url.clone(), credentials.clone());
}

Expand All @@ -117,7 +117,7 @@ impl CredentialsCache {
return None;
}

let mut realms = self.realms.lock().unwrap();
let mut realms = self.realms.write().unwrap();

// Always replace existing entries if we have a password
if credentials.password().is_some() {
Expand Down
21 changes: 10 additions & 11 deletions crates/uv-auth/src/keyring.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{collections::HashSet, sync::Mutex};
use std::collections::HashSet;
use std::sync::RwLock;

use tokio::process::Command;
use tracing::{debug, instrument, warn};
Expand All @@ -13,7 +14,7 @@ use crate::credentials::Credentials;
#[derive(Debug)]
pub struct KeyringProvider {
/// Tracks host and username pairs with no credentials to avoid repeated queries.
cache: Mutex<HashSet<(String, String)>>,
cache: RwLock<HashSet<(String, String)>>,
backend: KeyringProviderBackend,
}

Expand All @@ -29,7 +30,7 @@ impl KeyringProvider {
/// Create a new [`KeyringProvider::Subprocess`].
pub fn subprocess() -> Self {
Self {
cache: Mutex::new(HashSet::new()),
cache: RwLock::new(HashSet::new()),
backend: KeyringProviderBackend::Subprocess,
}
}
Expand Down Expand Up @@ -65,7 +66,7 @@ impl KeyringProvider {
// use-cases.
let key = (host.to_string(), username.to_string());
{
let cache = self.cache.lock().unwrap();
let cache = self.cache.read().unwrap();

if cache.contains(&key) {
debug!(
Expand Down Expand Up @@ -95,11 +96,9 @@ impl KeyringProvider {
};
}

{
let mut cache = self.cache.lock().unwrap();
if password.is_none() {
cache.insert(key);
}
if password.is_none() {
let mut cache = self.cache.write().unwrap();
cache.insert(key);
}

password.map(|password| Credentials::new(Some(username.to_string()), Some(password)))
Expand Down Expand Up @@ -148,7 +147,7 @@ impl KeyringProvider {
use std::collections::HashMap;

Self {
cache: Mutex::new(HashSet::new()),
cache: RwLock::new(HashSet::new()),
backend: KeyringProviderBackend::Dummy(HashMap::from_iter(
iter.into_iter()
.map(|((service, username), password)| ((service.into(), username), password)),
Expand All @@ -162,7 +161,7 @@ impl KeyringProvider {
use std::collections::HashMap;

Self {
cache: Mutex::new(HashSet::new()),
cache: RwLock::new(HashSet::new()),
backend: KeyringProviderBackend::Dummy(HashMap::new()),
}
}
Expand Down

0 comments on commit a07adf7

Please sign in to comment.