Skip to content

Commit 7aa799b

Browse files
committed
chore(*): finalize SCC 2.3.1
1 parent ad75430 commit 7aa799b

7 files changed

+105
-14
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
2.3.1
66

7+
* Fix a problem with `HashCache::read` and `HashMap::read` where the read lock is dropped too early: [#176](https://github.com/wvwwvwwv/scalable-concurrent-containers/issues/176).
78
* Fix `HashCache` documentation: [#175](https://github.com/wvwwvwwv/scalable-concurrent-containers/issues/175).
8-
* Implement `FromIterator` for collection types: [#173](https://github.com/wvwwvwwv/scalable-concurrent-containers/issues/173).
9+
* Implement `FromIterator` for `Hash*` types: [#173](https://github.com/wvwwvwwv/scalable-concurrent-containers/issues/173).
910

1011
2.3.0
1112

src/hash_cache.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ where
472472
self.read_entry(key, self.hash(key), &mut (), &Guard::new())
473473
.ok()
474474
.flatten()
475-
.map(|(k, v, r)| {
475+
.map(|(r, (k, v))| {
476476
let result = reader(k, v);
477477
drop(r);
478478
result
@@ -503,7 +503,7 @@ where
503503
let mut async_wait = AsyncWait::default();
504504
let mut async_wait_pinned = Pin::new(&mut async_wait);
505505
if let Ok(result) = self.read_entry(key, hash, &mut async_wait_pinned, &Guard::new()) {
506-
return result.map(|(k, v, r)| {
506+
return result.map(|(r, (k, v))| {
507507
let result = reader(k, v);
508508
drop(r);
509509
result
@@ -1205,6 +1205,27 @@ where
12051205
}
12061206
}
12071207

1208+
impl<K, V, H> FromIterator<(K, V)> for HashCache<K, V, H>
1209+
where
1210+
K: Eq + Hash,
1211+
H: BuildHasher + Default,
1212+
{
1213+
#[inline]
1214+
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
1215+
let into_iter = iter.into_iter();
1216+
let size_hint = into_iter.size_hint();
1217+
let hashcache = Self::with_capacity_and_hasher(
1218+
size_hint.0,
1219+
Self::capacity_from_size_hint(size_hint),
1220+
H::default(),
1221+
);
1222+
into_iter.for_each(|e| {
1223+
let _result = hashcache.put(e.0, e.1);
1224+
});
1225+
hashcache
1226+
}
1227+
}
1228+
12081229
impl<K, V, H> HashTable<K, V, H, DoublyLinkedList, CACHE> for HashCache<K, V, H>
12091230
where
12101231
K: Eq + Hash,

src/hash_index.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ where
756756
self.read_entry(key, self.hash(key), &mut (), guard)
757757
.ok()
758758
.flatten()
759-
.map(|(_, v, _)| v)
759+
.map(|(_, (_, v))| v)
760760
}
761761

762762
/// Peeks a key-value pair without acquiring locks.
@@ -783,7 +783,7 @@ where
783783
self.read_entry(key, self.hash(key), &mut (), &guard)
784784
.ok()
785785
.flatten()
786-
.map(|(k, v, _)| reader(k, v))
786+
.map(|(_, (k, v))| reader(k, v))
787787
}
788788

789789
/// Returns `true` if the [`HashIndex`] contains a value for the specified key.
@@ -1201,6 +1201,26 @@ where
12011201
}
12021202
}
12031203

1204+
impl<K, V, H> FromIterator<(K, V)> for HashIndex<K, V, H>
1205+
where
1206+
K: 'static + Clone + Eq + Hash,
1207+
V: 'static + Clone,
1208+
H: BuildHasher + Default,
1209+
{
1210+
#[inline]
1211+
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
1212+
let into_iter = iter.into_iter();
1213+
let hashindex = Self::with_capacity_and_hasher(
1214+
Self::capacity_from_size_hint(into_iter.size_hint()),
1215+
H::default(),
1216+
);
1217+
into_iter.for_each(|e| {
1218+
let _result = hashindex.insert(e.0, e.1);
1219+
});
1220+
hashindex
1221+
}
1222+
}
1223+
12041224
impl<K, V, H> HashTable<K, V, H, (), OPTIMISTIC> for HashIndex<K, V, H>
12051225
where
12061226
K: 'static + Clone + Eq + Hash,

src/hash_map.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ where
873873
self.read_entry(key, self.hash(key), &mut (), &Guard::new())
874874
.ok()
875875
.flatten()
876-
.map(|(k, v, r)| {
876+
.map(|(r, (k, v))| {
877877
let result = reader(k, v);
878878
drop(r);
879879
result
@@ -904,7 +904,7 @@ where
904904
let mut async_wait = AsyncWait::default();
905905
let mut async_wait_pinned = Pin::new(&mut async_wait);
906906
if let Ok(result) = self.read_entry(key, hash, &mut async_wait_pinned, &Guard::new()) {
907-
return result.map(|(k, v, r)| {
907+
return result.map(|(r, (k, v))| {
908908
let result = reader(k, v);
909909
drop(r);
910910
result

src/hash_set.rs

+20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! [`HashSet`] is a concurrent and asynchronous hash set.
22
3+
use super::hash_table::HashTable;
34
use super::{Equivalent, HashMap};
45
use std::collections::hash_map::RandomState;
56
use std::fmt::{self, Debug};
@@ -736,6 +737,25 @@ where
736737
}
737738
}
738739

740+
impl<K, H> FromIterator<K> for HashSet<K, H>
741+
where
742+
K: Eq + Hash,
743+
H: BuildHasher + Default,
744+
{
745+
#[inline]
746+
fn from_iter<T: IntoIterator<Item = K>>(iter: T) -> Self {
747+
let into_iter = iter.into_iter();
748+
let hashset = Self::with_capacity_and_hasher(
749+
HashMap::<K, (), H>::capacity_from_size_hint(into_iter.size_hint()),
750+
H::default(),
751+
);
752+
into_iter.for_each(|k| {
753+
let _result = hashset.insert(k);
754+
});
755+
hashset
756+
}
757+
}
758+
739759
impl<K, H> PartialEq for HashSet<K, H>
740760
where
741761
K: Eq + Hash,

src/hash_table.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,15 @@ where
282282
}
283283

284284
/// Reads an entry from the [`HashTable`].
285+
#[allow(clippy::type_complexity)]
285286
#[inline]
286287
fn read_entry<'g, Q, D>(
287288
&self,
288289
key: &Q,
289290
hash: u64,
290291
async_wait: &mut D,
291292
guard: &'g Guard,
292-
) -> Result<Option<(&'g K, &'g V, Option<Reader<'g, K, V, L, TYPE>>)>, ()>
293+
) -> Result<Option<(Option<Reader<'g, K, V, L, TYPE>>, &'g (K, V))>, ()>
293294
where
294295
Q: Equivalent<K> + Hash + ?Sized,
295296
D: DeriveAsyncWait,
@@ -302,13 +303,13 @@ where
302303
!= Ok(true)
303304
{
304305
let index = old_array.calculate_bucket_index(hash);
305-
if let Some((k, v)) = old_array.bucket(index).search_entry(
306+
if let Some(entry) = old_array.bucket(index).search_entry(
306307
old_array.data_block(index),
307308
key,
308309
BucketArray::<K, V, L, TYPE>::partial_hash(hash),
309310
guard,
310311
) {
311-
return Ok(Some((k, v, None)));
312+
return Ok(Some((None, entry)));
312313
}
313314
}
314315
} else {
@@ -325,7 +326,7 @@ where
325326
BucketArray::<K, V, L, TYPE>::partial_hash(hash),
326327
guard,
327328
) {
328-
return Ok(Some((&entry.0, &entry.1, None)));
329+
return Ok(Some((None, entry)));
329330
}
330331
} else {
331332
let lock_result = if let Some(async_wait) = async_wait.derive() {
@@ -334,13 +335,13 @@ where
334335
Reader::lock(bucket, guard)
335336
};
336337
if let Some(reader) = lock_result {
337-
if let Some((key, val)) = reader.search_entry(
338+
if let Some(entry) = reader.search_entry(
338339
current_array.data_block(index),
339340
key,
340341
BucketArray::<K, V, L, TYPE>::partial_hash(hash),
341342
guard,
342343
) {
343-
return Ok(Some((key, val, Some(reader))));
344+
return Ok(Some((Some(reader), entry)));
344345
}
345346
}
346347
}

src/tests/correctness.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ mod hashmap_test {
172172
assert_eq!(first_item.unwrap(), &123_u8);
173173
}
174174
barrier_clone.wait();
175-
thread::sleep(Duration::from_millis(100));
175+
thread::sleep(Duration::from_millis(16));
176176
{
177177
let first_item = value.get(0);
178178
assert_eq!(first_item.unwrap(), &123_u8);
@@ -1007,6 +1007,24 @@ mod hashindex_test {
10071007
}
10081008
}
10091009

1010+
#[test]
1011+
fn from_iter() {
1012+
static INST_CNT: AtomicUsize = AtomicUsize::new(0);
1013+
1014+
let workload_size = 256;
1015+
let hashindex = (0..workload_size)
1016+
.into_iter()
1017+
.map(|k| (k / 2, R::new(&INST_CNT)))
1018+
.collect::<HashIndex<usize, R>>();
1019+
assert_eq!(hashindex.len(), workload_size / 2);
1020+
drop(hashindex);
1021+
1022+
while INST_CNT.load(Relaxed) != 0 {
1023+
Guard::new().accelerate();
1024+
thread::yield_now();
1025+
}
1026+
}
1027+
10101028
#[test]
10111029
fn clone() {
10121030
static INST_CNT: AtomicUsize = AtomicUsize::new(0);
@@ -1553,6 +1571,16 @@ mod hashset_test {
15531571
assert!(hashset.contains("HELLO"));
15541572
}
15551573

1574+
#[test]
1575+
fn from_iter() {
1576+
let workload_size = 256;
1577+
let hashset = (0..workload_size)
1578+
.into_iter()
1579+
.map(|k| k / 2)
1580+
.collect::<HashSet<usize>>();
1581+
assert_eq!(hashset.len(), workload_size / 2);
1582+
}
1583+
15561584
#[test]
15571585
fn compare() {
15581586
let hashset1: HashSet<String> = HashSet::new();

0 commit comments

Comments
 (0)