Skip to content

Commit 23461b2

Browse files
committedAug 16, 2021
Auto merge of rust-lang#87696 - ssomers:btree_lazy_iterator_cleanup, r=Mark-Simulacrum
BTree: merge the complication introduced by rust-lang#81486 and rust-lang#86031 Also: - Deallocate the last few tree nodes as soon as an `into_iter` iterator steps beyond the end, instead of waiting around for the drop of the iterator (just to share more code). - Symmetric code for backward iteration. - Mark unsafe the methods on dying handles, modelling dying handles after raw pointers: it's the caller's responsibility to use them safely. r? `@Mark-Simulacrum`
2 parents 2a6fb9a + 7b28036 commit 23461b2

File tree

3 files changed

+56
-56
lines changed

3 files changed

+56
-56
lines changed
 

‎library/alloc/src/collections/btree/map.rs

+40-50
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,7 @@ pub struct BTreeMap<K, V> {
161161
#[stable(feature = "btree_drop", since = "1.7.0")]
162162
unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap<K, V> {
163163
fn drop(&mut self) {
164-
if let Some(root) = self.root.take() {
165-
Dropper { front: root.into_dying().first_leaf_edge(), remaining_length: self.length };
166-
}
164+
drop(unsafe { ptr::read(self) }.into_iter())
167165
}
168166
}
169167

@@ -352,14 +350,6 @@ impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for IntoIter<K, V> {
352350
}
353351
}
354352

355-
/// A simplified version of `IntoIter` that is not double-ended and has only one
356-
/// purpose: to drop the remainder of an `IntoIter`. Therefore it also serves to
357-
/// drop an entire tree without the need to first look up a `back` leaf edge.
358-
struct Dropper<K, V> {
359-
front: Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>,
360-
remaining_length: usize,
361-
}
362-
363353
/// An iterator over the keys of a `BTreeMap`.
364354
///
365355
/// This `struct` is created by the [`keys`] method on [`BTreeMap`]. See its
@@ -1458,47 +1448,57 @@ impl<K, V> IntoIterator for BTreeMap<K, V> {
14581448
}
14591449
}
14601450

1461-
impl<K, V> Drop for Dropper<K, V> {
1451+
#[stable(feature = "btree_drop", since = "1.7.0")]
1452+
impl<K, V> Drop for IntoIter<K, V> {
14621453
fn drop(&mut self) {
1463-
// Similar to advancing a non-fusing iterator.
1464-
fn next_or_end<K, V>(
1465-
this: &mut Dropper<K, V>,
1466-
) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV>>
1467-
{
1468-
if this.remaining_length == 0 {
1469-
unsafe { ptr::read(&this.front).deallocating_end() }
1470-
None
1471-
} else {
1472-
this.remaining_length -= 1;
1473-
Some(unsafe { this.front.deallocating_next_unchecked() })
1474-
}
1475-
}
1476-
1477-
struct DropGuard<'a, K, V>(&'a mut Dropper<K, V>);
1454+
struct DropGuard<'a, K, V>(&'a mut IntoIter<K, V>);
14781455

14791456
impl<'a, K, V> Drop for DropGuard<'a, K, V> {
14801457
fn drop(&mut self) {
14811458
// Continue the same loop we perform below. This only runs when unwinding, so we
14821459
// don't have to care about panics this time (they'll abort).
1483-
while let Some(kv) = next_or_end(&mut self.0) {
1484-
kv.drop_key_val();
1460+
while let Some(kv) = self.0.dying_next() {
1461+
// SAFETY: we consume the dying handle immediately.
1462+
unsafe { kv.drop_key_val() };
14851463
}
14861464
}
14871465
}
14881466

1489-
while let Some(kv) = next_or_end(self) {
1467+
while let Some(kv) = self.dying_next() {
14901468
let guard = DropGuard(self);
1491-
kv.drop_key_val();
1469+
// SAFETY: we don't touch the tree before consuming the dying handle.
1470+
unsafe { kv.drop_key_val() };
14921471
mem::forget(guard);
14931472
}
14941473
}
14951474
}
14961475

1497-
#[stable(feature = "btree_drop", since = "1.7.0")]
1498-
impl<K, V> Drop for IntoIter<K, V> {
1499-
fn drop(&mut self) {
1500-
if let Some(front) = self.range.take_front() {
1501-
Dropper { front, remaining_length: self.length };
1476+
impl<K, V> IntoIter<K, V> {
1477+
/// Core of a `next` method returning a dying KV handle,
1478+
/// invalidated by further calls to this function and some others.
1479+
fn dying_next(
1480+
&mut self,
1481+
) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV>> {
1482+
if self.length == 0 {
1483+
self.range.deallocating_end();
1484+
None
1485+
} else {
1486+
self.length -= 1;
1487+
Some(unsafe { self.range.deallocating_next_unchecked() })
1488+
}
1489+
}
1490+
1491+
/// Core of a `next_back` method returning a dying KV handle,
1492+
/// invalidated by further calls to this function and some others.
1493+
fn dying_next_back(
1494+
&mut self,
1495+
) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV>> {
1496+
if self.length == 0 {
1497+
self.range.deallocating_end();
1498+
None
1499+
} else {
1500+
self.length -= 1;
1501+
Some(unsafe { self.range.deallocating_next_back_unchecked() })
15021502
}
15031503
}
15041504
}
@@ -1508,13 +1508,8 @@ impl<K, V> Iterator for IntoIter<K, V> {
15081508
type Item = (K, V);
15091509

15101510
fn next(&mut self) -> Option<(K, V)> {
1511-
if self.length == 0 {
1512-
None
1513-
} else {
1514-
self.length -= 1;
1515-
let kv = unsafe { self.range.deallocating_next_unchecked() };
1516-
Some(kv.into_key_val())
1517-
}
1511+
// SAFETY: we consume the dying handle immediately.
1512+
self.dying_next().map(unsafe { |kv| kv.into_key_val() })
15181513
}
15191514

15201515
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -1525,13 +1520,8 @@ impl<K, V> Iterator for IntoIter<K, V> {
15251520
#[stable(feature = "rust1", since = "1.0.0")]
15261521
impl<K, V> DoubleEndedIterator for IntoIter<K, V> {
15271522
fn next_back(&mut self) -> Option<(K, V)> {
1528-
if self.length == 0 {
1529-
None
1530-
} else {
1531-
self.length -= 1;
1532-
let kv = unsafe { self.range.deallocating_next_back_unchecked() };
1533-
Some(kv.into_key_val())
1534-
}
1523+
// SAFETY: we consume the dying handle immediately.
1524+
self.dying_next_back().map(unsafe { |kv| kv.into_key_val() })
15351525
}
15361526
}
15371527

‎library/alloc/src/collections/btree/navigate.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,7 @@ impl<'a, K, V> LazyLeafRange<marker::ValMut<'a>, K, V> {
167167
}
168168

169169
impl<K, V> LazyLeafRange<marker::Dying, K, V> {
170-
#[inline]
171-
pub fn take_front(
170+
fn take_front(
172171
&mut self,
173172
) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>> {
174173
match self.front.take()? {
@@ -194,6 +193,13 @@ impl<K, V> LazyLeafRange<marker::Dying, K, V> {
194193
let back = self.init_back().unwrap();
195194
unsafe { back.deallocating_next_back_unchecked() }
196195
}
196+
197+
#[inline]
198+
pub fn deallocating_end(&mut self) {
199+
if let Some(front) = self.take_front() {
200+
front.deallocating_end()
201+
}
202+
}
197203
}
198204

199205
impl<BorrowType: marker::BorrowType, K, V> LazyLeafRange<BorrowType, K, V> {
@@ -488,7 +494,7 @@ impl<K, V> Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge> {
488494
/// both sides of the tree, and have hit the same edge. As it is intended
489495
/// only to be called when all keys and values have been returned,
490496
/// no cleanup is done on any of the keys or values.
491-
pub fn deallocating_end(self) {
497+
fn deallocating_end(self) {
492498
let mut edge = self.forget_node_type();
493499
while let Some(parent_edge) = unsafe { edge.into_node().deallocate_and_ascend() } {
494500
edge = parent_edge.forget_node_type();
@@ -565,7 +571,7 @@ impl<K, V> Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge> {
565571
///
566572
/// The only safe way to proceed with the updated handle is to compare it, drop it,
567573
/// or call this method or counterpart `deallocating_next_back_unchecked` again.
568-
pub unsafe fn deallocating_next_unchecked(
574+
unsafe fn deallocating_next_unchecked(
569575
&mut self,
570576
) -> Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV> {
571577
super::mem::replace(self, |leaf_edge| unsafe { leaf_edge.deallocating_next().unwrap() })

‎library/alloc/src/collections/btree/node.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,9 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>
10581058

10591059
impl<K, V, NodeType> Handle<NodeRef<marker::Dying, K, V, NodeType>, marker::KV> {
10601060
/// Extracts the key and value that the KV handle refers to.
1061-
pub fn into_key_val(mut self) -> (K, V) {
1061+
/// # Safety
1062+
/// The node that the handle refers to must not yet have been deallocated.
1063+
pub unsafe fn into_key_val(mut self) -> (K, V) {
10621064
debug_assert!(self.idx < self.node.len());
10631065
let leaf = self.node.as_leaf_dying();
10641066
unsafe {
@@ -1069,8 +1071,10 @@ impl<K, V, NodeType> Handle<NodeRef<marker::Dying, K, V, NodeType>, marker::KV>
10691071
}
10701072

10711073
/// Drops the key and value that the KV handle refers to.
1074+
/// # Safety
1075+
/// The node that the handle refers to must not yet have been deallocated.
10721076
#[inline]
1073-
pub fn drop_key_val(mut self) {
1077+
pub unsafe fn drop_key_val(mut self) {
10741078
debug_assert!(self.idx < self.node.len());
10751079
let leaf = self.node.as_leaf_dying();
10761080
unsafe {

0 commit comments

Comments
 (0)
Please sign in to comment.