Skip to content

Commit 25e60a2

Browse files
committed
Auto merge of #439 - Amanieu:fix_alloc_leak, r=Amanieu
Fix leaking of allocator in `RawIntoIter` and `RawIntoParIter` The allocator was not getting properly dropped when it was moved into the iterator. Fixes #438
2 parents 3056ee9 + efdc159 commit 25e60a2

File tree

3 files changed

+71
-12
lines changed

3 files changed

+71
-12
lines changed

src/external_trait_impls/rayon/raw.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::raw::Bucket;
22
use crate::raw::{Allocator, Global, RawIter, RawIterRange, RawTable};
33
use crate::scopeguard::guard;
4-
use alloc::alloc::dealloc;
54
use core::marker::PhantomData;
65
use core::mem;
76
use core::ptr::NonNull;
@@ -97,9 +96,9 @@ impl<T: Send, A: Allocator + Clone + Send> ParallelIterator for RawIntoParIter<T
9796
{
9897
let iter = unsafe { self.table.iter().iter };
9998
let _guard = guard(self.table.into_allocation(), |alloc| {
100-
if let Some((ptr, layout)) = *alloc {
99+
if let Some((ptr, layout, ref alloc)) = *alloc {
101100
unsafe {
102-
dealloc(ptr.as_ptr(), layout);
101+
alloc.deallocate(ptr, layout);
103102
}
104103
}
105104
});

src/map.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6653,6 +6653,12 @@ mod test_map {
66536653
use super::Entry::{Occupied, Vacant};
66546654
use super::EntryRef;
66556655
use super::{HashMap, RawEntryMut};
6656+
use alloc::string::ToString;
6657+
use alloc::sync::Arc;
6658+
use allocator_api2::alloc::{AllocError, Allocator, Global};
6659+
use core::alloc::Layout;
6660+
use core::ptr::NonNull;
6661+
use core::sync::atomic::{AtomicBool, Ordering};
66566662
use rand::{rngs::SmallRng, Rng, SeedableRng};
66576663
use std::borrow::ToOwned;
66586664
use std::cell::RefCell;
@@ -8503,4 +8509,60 @@ mod test_map {
85038509
);
85048510
let _map2 = map1.clone();
85058511
}
8512+
8513+
#[test]
8514+
fn test_hashmap_into_iter_bug() {
8515+
let dropped: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
8516+
8517+
{
8518+
struct MyAllocInner {
8519+
drop_flag: Arc<AtomicBool>,
8520+
}
8521+
8522+
#[derive(Clone)]
8523+
struct MyAlloc {
8524+
_inner: Arc<MyAllocInner>,
8525+
}
8526+
8527+
impl Drop for MyAllocInner {
8528+
fn drop(&mut self) {
8529+
println!("MyAlloc freed.");
8530+
self.drop_flag.store(true, Ordering::SeqCst);
8531+
}
8532+
}
8533+
8534+
unsafe impl Allocator for MyAlloc {
8535+
fn allocate(
8536+
&self,
8537+
layout: Layout,
8538+
) -> std::result::Result<NonNull<[u8]>, AllocError> {
8539+
let g = Global;
8540+
g.allocate(layout)
8541+
}
8542+
8543+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
8544+
let g = Global;
8545+
g.deallocate(ptr, layout)
8546+
}
8547+
}
8548+
8549+
let mut map = crate::HashMap::with_capacity_in(
8550+
10,
8551+
MyAlloc {
8552+
_inner: Arc::new(MyAllocInner {
8553+
drop_flag: dropped.clone(),
8554+
}),
8555+
},
8556+
);
8557+
for i in 0..10 {
8558+
map.entry(i).or_insert_with(|| "i".to_string());
8559+
}
8560+
8561+
for (k, v) in map {
8562+
println!("{}, {}", k, v);
8563+
}
8564+
}
8565+
8566+
assert!(dropped.load(Ordering::SeqCst));
8567+
}
85068568
}

src/raw/mod.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,20 +1482,18 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
14821482
pub unsafe fn into_iter_from(self, iter: RawIter<T>) -> RawIntoIter<T, A> {
14831483
debug_assert_eq!(iter.len(), self.len());
14841484

1485-
let alloc = self.table.alloc.clone();
14861485
let allocation = self.into_allocation();
14871486
RawIntoIter {
14881487
iter,
14891488
allocation,
14901489
marker: PhantomData,
1491-
alloc,
14921490
}
14931491
}
14941492

14951493
/// Converts the table into a raw allocation. The contents of the table
14961494
/// should be dropped using a `RawIter` before freeing the allocation.
14971495
#[cfg_attr(feature = "inline-more", inline)]
1498-
pub(crate) fn into_allocation(self) -> Option<(NonNull<u8>, Layout)> {
1496+
pub(crate) fn into_allocation(self) -> Option<(NonNull<u8>, Layout, A)> {
14991497
let alloc = if self.table.is_empty_singleton() {
15001498
None
15011499
} else {
@@ -1508,6 +1506,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
15081506
Some((
15091507
unsafe { NonNull::new_unchecked(self.table.ctrl.as_ptr().sub(ctrl_offset)) },
15101508
layout,
1509+
unsafe { ptr::read(&self.table.alloc) },
15111510
))
15121511
};
15131512
mem::forget(self);
@@ -3069,9 +3068,8 @@ impl<T> FusedIterator for RawIter<T> {}
30693068
/// Iterator which consumes a table and returns elements.
30703069
pub struct RawIntoIter<T, A: Allocator + Clone = Global> {
30713070
iter: RawIter<T>,
3072-
allocation: Option<(NonNull<u8>, Layout)>,
3071+
allocation: Option<(NonNull<u8>, Layout, A)>,
30733072
marker: PhantomData<T>,
3074-
alloc: A,
30753073
}
30763074

30773075
impl<T, A: Allocator + Clone> RawIntoIter<T, A> {
@@ -3103,8 +3101,8 @@ unsafe impl<#[may_dangle] T, A: Allocator + Clone> Drop for RawIntoIter<T, A> {
31033101
self.iter.drop_elements();
31043102

31053103
// Free the table
3106-
if let Some((ptr, layout)) = self.allocation {
3107-
self.alloc.deallocate(ptr, layout);
3104+
if let Some((ptr, layout, ref alloc)) = self.allocation {
3105+
alloc.deallocate(ptr, layout);
31083106
}
31093107
}
31103108
}
@@ -3118,8 +3116,8 @@ impl<T, A: Allocator + Clone> Drop for RawIntoIter<T, A> {
31183116
self.iter.drop_elements();
31193117

31203118
// Free the table
3121-
if let Some((ptr, layout)) = self.allocation {
3122-
self.alloc.deallocate(ptr, layout);
3119+
if let Some((ptr, layout, ref alloc)) = self.allocation {
3120+
alloc.deallocate(ptr, layout);
31233121
}
31243122
}
31253123
}

0 commit comments

Comments
 (0)