Convert Repository types to use interior mutability
This changes `Repository` to use interior mutability
in order to store state. At the moment, `EphemeralRepository`
is the only repository type that stores state. By switching
over to internally using `Arc<Mutex<...>>>` to wrap the state,
it allows repositories to be used concurrently across threads.
This trait will be especially important as we transition
over to using asynchronous io with Hyper 0.11+.
diff --git a/src/client.rs b/src/client.rs
index fe83d2c..5a9cb85 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -133,7 +133,7 @@
///
/// **WARNING**: This method offers weaker security guarantees than the related method
/// `with_root_pinned`.
- pub fn new(config: Config<T>, mut local: L, remote: R) -> Result<Self> {
+ pub fn new(config: Config<T>, local: L, remote: R) -> Result<Self> {
let root = local
.fetch_metadata(
&MetadataPath::from_role(&Role::Root),
@@ -169,8 +169,8 @@
pub fn with_root_pinned<'a, I>(
trusted_root_keys: I,
config: Config<T>,
- mut local: L,
- mut remote: R,
+ local: L,
+ remote: R,
) -> Result<Self>
where
I: IntoIterator<Item = &'a KeyId>,
@@ -776,7 +776,7 @@
#[test]
fn root_chain_update() {
- let mut repo = EphemeralRepository::new();
+ let repo = EphemeralRepository::new();
let root = RootMetadata::new(
1,
Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
diff --git a/src/repository.rs b/src/repository.rs
index 38e5191..1bfb705 100644
--- a/src/repository.rs
+++ b/src/repository.rs
@@ -9,6 +9,7 @@
use std::io::{Cursor, Read, Write};
use std::marker::PhantomData;
use std::path::PathBuf;
+use std::sync::{Arc, RwLock};
use tempfile::NamedTempFile;
use crypto::{self, HashAlgorithm, HashValue};
@@ -34,7 +35,7 @@
/// Note: This **MUST** canonicalize the bytes before storing them as a read will expect the
/// hashes of the metadata to match.
fn store_metadata<M>(
- &mut self,
+ &self,
meta_path: &MetadataPath,
version: &MetadataVersion,
metadata: &SignedMetadata<D, M>,
@@ -44,7 +45,7 @@
/// Fetch signed metadata.
fn fetch_metadata<M>(
- &mut self,
+ &self,
meta_path: &MetadataPath,
version: &MetadataVersion,
max_size: &Option<usize>,
@@ -55,13 +56,13 @@
M: Metadata;
/// Store the given target.
- fn store_target<R>(&mut self, read: R, target_path: &TargetPath) -> Result<()>
+ fn store_target<R>(&self, read: R, target_path: &TargetPath) -> Result<()>
where
R: Read;
/// Fetch the given target.
fn fetch_target(
- &mut self,
+ &self,
target_path: &TargetPath,
target_description: &TargetDescription,
min_bytes_per_second: u32,
@@ -119,7 +120,7 @@
type TargetRead = File;
fn store_metadata<M>(
- &mut self,
+ &self,
meta_path: &MetadataPath,
version: &MetadataVersion,
metadata: &SignedMetadata<D, M>,
@@ -151,7 +152,7 @@
/// Fetch signed metadata.
fn fetch_metadata<M>(
- &mut self,
+ &self,
meta_path: &MetadataPath,
version: &MetadataVersion,
max_size: &Option<usize>,
@@ -176,7 +177,7 @@
Ok(D::from_reader(read)?)
}
- fn store_target<R>(&mut self, mut read: R, target_path: &TargetPath) -> Result<()>
+ fn store_target<R>(&self, mut read: R, target_path: &TargetPath) -> Result<()>
where
R: Read,
{
@@ -206,7 +207,7 @@
}
fn fetch_target(
- &mut self,
+ &self,
target_path: &TargetPath,
target_description: &TargetDescription,
min_bytes_per_second: u32,
@@ -316,7 +317,7 @@
/// This always returns `Err` as storing over HTTP is not yet supported.
fn store_metadata<M>(
- &mut self,
+ &self,
_: &MetadataPath,
_: &MetadataVersion,
_: &SignedMetadata<D, M>,
@@ -330,7 +331,7 @@
}
fn fetch_metadata<M>(
- &mut self,
+ &self,
meta_path: &MetadataPath,
version: &MetadataVersion,
max_size: &Option<usize>,
@@ -354,7 +355,7 @@
}
/// This always returns `Err` as storing over HTTP is not yet supported.
- fn store_target<R>(&mut self, _: R, _: &TargetPath) -> Result<()>
+ fn store_target<R>(&self, _: R, _: &TargetPath) -> Result<()>
where
R: Read,
{
@@ -362,7 +363,7 @@
}
fn fetch_target(
- &mut self,
+ &self,
target_path: &TargetPath,
target_description: &TargetDescription,
min_bytes_per_second: u32,
@@ -383,8 +384,8 @@
where
D: DataInterchange,
{
- metadata: HashMap<(MetadataPath, MetadataVersion), Vec<u8>>,
- targets: HashMap<TargetPath, Vec<u8>>,
+ metadata: Arc<RwLock<HashMap<(MetadataPath, MetadataVersion), Vec<u8>>>>,
+ targets: Arc<RwLock<HashMap<TargetPath, Vec<u8>>>>,
interchange: PhantomData<D>,
}
@@ -395,8 +396,8 @@
/// Create a new ephemercal repository.
pub fn new() -> Self {
EphemeralRepository {
- metadata: HashMap::new(),
- targets: HashMap::new(),
+ metadata: Arc::new(RwLock::new(HashMap::new())),
+ targets: Arc::new(RwLock::new(HashMap::new())),
interchange: PhantomData,
}
}
@@ -418,7 +419,7 @@
type TargetRead = Cursor<Vec<u8>>;
fn store_metadata<M>(
- &mut self,
+ &self,
meta_path: &MetadataPath,
version: &MetadataVersion,
metadata: &SignedMetadata<D, M>,
@@ -429,13 +430,13 @@
Self::check::<M>(meta_path)?;
let mut buf = Vec::new();
D::to_writer(&mut buf, metadata)?;
- let _ = self.metadata
- .insert((meta_path.clone(), version.clone()), buf);
+ let mut metadata = self.metadata.write().unwrap();
+ let _ = metadata.insert((meta_path.clone(), version.clone()), buf);
Ok(())
}
fn fetch_metadata<M>(
- &mut self,
+ &self,
meta_path: &MetadataPath,
version: &MetadataVersion,
max_size: &Option<usize>,
@@ -447,7 +448,8 @@
{
Self::check::<M>(meta_path)?;
- match self.metadata.get(&(meta_path.clone(), version.clone())) {
+ let metadata = self.metadata.read().unwrap();
+ match metadata.get(&(meta_path.clone(), version.clone())) {
Some(bytes) => {
let reader = SafeReader::new(
&**bytes,
@@ -461,23 +463,25 @@
}
}
- fn store_target<R>(&mut self, mut read: R, target_path: &TargetPath) -> Result<()>
+ fn store_target<R>(&self, mut read: R, target_path: &TargetPath) -> Result<()>
where
R: Read,
{
let mut buf = Vec::new();
read.read_to_end(&mut buf)?;
- let _ = self.targets.insert(target_path.clone(), buf);
+ let mut targets = self.targets.write().unwrap();
+ let _ = targets.insert(target_path.clone(), buf);
Ok(())
}
fn fetch_target(
- &mut self,
+ &self,
target_path: &TargetPath,
target_description: &TargetDescription,
min_bytes_per_second: u32,
) -> Result<SafeReader<Self::TargetRead>> {
- match self.targets.get(target_path) {
+ let targets = self.targets.read().unwrap();
+ match targets.get(target_path) {
Some(bytes) => {
let cur = Cursor::new(bytes.clone());
let (alg, value) = crypto::hash_preference(target_description.hashes())?;
@@ -502,7 +506,7 @@
#[test]
fn ephemeral_repo_targets() {
- let mut repo = EphemeralRepository::<Json>::new();
+ let repo = EphemeralRepository::<Json>::new();
let data: &[u8] = b"like tears in the rain";
let target_description =
@@ -524,7 +528,7 @@
#[test]
fn file_system_repo_targets() {
let temp_dir = TempDir::new("rust-tuf").unwrap();
- let mut repo = FileSystemRepository::<Json>::new(temp_dir.path().to_path_buf()).unwrap();
+ let repo = FileSystemRepository::<Json>::new(temp_dir.path().to_path_buf()).unwrap();
// test that init worked
assert!(temp_dir.path().join("metadata").exists());