Skip to content

Commit e595656

Browse files
authored
Merge pull request #64 from jamesmunns/fix-free-insertion
Fix free logic
2 parents cf6a569 + 2878c05 commit e595656

File tree

4 files changed

+287
-81
lines changed

4 files changed

+287
-81
lines changed

Changelog.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
- Fixed logic for freeing nodes ([#64])
4+
5+
[#64]: https://github.com/rust-osdev/linked-list-allocator/pull/64
6+
37
# 0.10.0 – 2022-06-27
48

59
- Changed constructor to take `*mut u8` instead of `usize` ([#62])

src/hole.rs

+123-46
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::alloc::Layout;
2-
use core::convert::TryInto;
32
use core::mem;
43
use core::mem::{align_of, size_of};
4+
use core::ptr::null_mut;
55
use core::ptr::NonNull;
66

77
use crate::align_up_size;
@@ -11,11 +11,27 @@ use super::align_up;
1111
/// A sorted list of holes. It uses the the holes itself to store its nodes.
1212
pub struct HoleList {
1313
pub(crate) first: Hole, // dummy
14+
pub(crate) bottom: *mut u8,
15+
pub(crate) top: *mut u8,
1416
}
1517

1618
pub(crate) struct Cursor {
1719
prev: NonNull<Hole>,
1820
hole: NonNull<Hole>,
21+
top: *mut u8,
22+
}
23+
24+
/// A block containing free memory. It points to the next hole and thus forms a linked list.
25+
pub(crate) struct Hole {
26+
pub size: usize,
27+
pub next: Option<NonNull<Hole>>,
28+
}
29+
30+
/// Basic information about a hole.
31+
#[derive(Debug, Clone, Copy)]
32+
struct HoleInfo {
33+
addr: *mut u8,
34+
size: usize,
1935
}
2036

2137
impl Cursor {
@@ -24,6 +40,7 @@ impl Cursor {
2440
self.hole.as_mut().next.map(|nhole| Cursor {
2541
prev: self.hole,
2642
hole: nhole,
43+
top: self.top,
2744
})
2845
}
2946
}
@@ -133,7 +150,9 @@ impl Cursor {
133150
////////////////////////////////////////////////////////////////////////////
134151
// This is where we actually perform surgery on the linked list.
135152
////////////////////////////////////////////////////////////////////////////
136-
let Cursor { mut prev, mut hole } = self;
153+
let Cursor {
154+
mut prev, mut hole, ..
155+
} = self;
137156
// Remove the current location from the previous node
138157
unsafe {
139158
prev.as_mut().next = None;
@@ -200,6 +219,41 @@ impl Cursor {
200219
}
201220
}
202221

222+
// See if we can extend this hole towards the end of the allocation region
223+
// If so: increase the size of the node. If no: keep the node as-is
224+
fn check_merge_top(mut node: NonNull<Hole>, top: *mut u8) {
225+
let node_u8 = node.as_ptr().cast::<u8>();
226+
let node_sz = unsafe { node.as_ref().size };
227+
228+
// If this is the last node, we need to see if we need to merge to the end
229+
let end = node_u8.wrapping_add(node_sz);
230+
let hole_layout = Layout::new::<Hole>();
231+
if end < top {
232+
let next_hole_end = align_up(end, hole_layout.align()).wrapping_add(hole_layout.size());
233+
234+
if next_hole_end > top {
235+
let offset = (top as usize) - (end as usize);
236+
unsafe {
237+
node.as_mut().size += offset;
238+
}
239+
}
240+
}
241+
}
242+
243+
// See if we can scoot this hole back to the bottom of the allocation region
244+
// If so: create and return the new hole. If not: return the existing hole
245+
fn check_merge_bottom(node: NonNull<Hole>, bottom: *mut u8) -> NonNull<Hole> {
246+
debug_assert_eq!(bottom as usize % align_of::<Hole>(), 0);
247+
248+
if bottom.wrapping_add(core::mem::size_of::<Hole>()) > node.as_ptr().cast::<u8>() {
249+
let offset = (node.as_ptr() as usize) - (bottom as usize);
250+
let size = unsafe { node.as_ref() }.size + offset;
251+
unsafe { make_hole(bottom, size) }
252+
} else {
253+
node
254+
}
255+
}
256+
203257
impl HoleList {
204258
/// Creates an empty `HoleList`.
205259
#[cfg(not(feature = "const_mut_refs"))]
@@ -209,6 +263,8 @@ impl HoleList {
209263
size: 0,
210264
next: None,
211265
},
266+
bottom: null_mut(),
267+
top: null_mut(),
212268
}
213269
}
214270

@@ -220,6 +276,8 @@ impl HoleList {
220276
size: 0,
221277
next: None,
222278
},
279+
bottom: null_mut(),
280+
top: null_mut(),
223281
}
224282
}
225283

@@ -228,6 +286,7 @@ impl HoleList {
228286
Some(Cursor {
229287
hole,
230288
prev: NonNull::new(&mut self.first)?,
289+
top: self.top,
231290
})
232291
} else {
233292
None
@@ -274,8 +333,7 @@ impl HoleList {
274333
let aligned_hole_addr = align_up(hole_addr, align_of::<Hole>());
275334
let ptr = aligned_hole_addr as *mut Hole;
276335
ptr.write(Hole {
277-
size: hole_size
278-
.saturating_sub(aligned_hole_addr.offset_from(hole_addr).try_into().unwrap()),
336+
size: hole_size - ((aligned_hole_addr as usize) - (hole_addr as usize)),
279337
next: None,
280338
});
281339

@@ -284,6 +342,8 @@ impl HoleList {
284342
size: 0,
285343
next: Some(NonNull::new_unchecked(ptr)),
286344
},
345+
bottom: aligned_hole_addr,
346+
top: hole_addr.wrapping_add(hole_size),
287347
}
288348
}
289349

@@ -370,19 +430,6 @@ impl HoleList {
370430
}
371431
}
372432

373-
/// A block containing free memory. It points to the next hole and thus forms a linked list.
374-
pub(crate) struct Hole {
375-
pub size: usize,
376-
pub next: Option<NonNull<Hole>>,
377-
}
378-
379-
/// Basic information about a hole.
380-
#[derive(Debug, Clone, Copy)]
381-
struct HoleInfo {
382-
addr: *mut u8,
383-
size: usize,
384-
}
385-
386433
unsafe fn make_hole(addr: *mut u8, size: usize) -> NonNull<Hole> {
387434
let hole_addr = addr.cast::<Hole>();
388435
debug_assert_eq!(
@@ -395,7 +442,7 @@ unsafe fn make_hole(addr: *mut u8, size: usize) -> NonNull<Hole> {
395442
}
396443

397444
impl Cursor {
398-
fn try_insert_back(self, mut node: NonNull<Hole>) -> Result<Self, Self> {
445+
fn try_insert_back(self, node: NonNull<Hole>, bottom: *mut u8) -> Result<Self, Self> {
399446
// Covers the case where the new hole exists BEFORE the current pointer,
400447
// which only happens when previous is the stub pointer
401448
if node < self.hole {
@@ -409,59 +456,86 @@ impl Cursor {
409456
);
410457
debug_assert_eq!(self.previous().size, 0);
411458

412-
let Cursor { mut prev, hole } = self;
459+
let Cursor {
460+
mut prev,
461+
hole,
462+
top,
463+
} = self;
413464
unsafe {
465+
let mut node = check_merge_bottom(node, bottom);
414466
prev.as_mut().next = Some(node);
415467
node.as_mut().next = Some(hole);
416468
}
417-
Ok(Cursor { prev, hole: node })
469+
Ok(Cursor {
470+
prev,
471+
hole: node,
472+
top,
473+
})
418474
} else {
419475
Err(self)
420476
}
421477
}
422478

423479
fn try_insert_after(&mut self, mut node: NonNull<Hole>) -> Result<(), ()> {
424-
if self.hole < node {
425-
let node_u8 = node.as_ptr().cast::<u8>();
426-
let node_size = unsafe { node.as_ref().size };
427-
let hole_u8 = self.hole.as_ptr().cast::<u8>();
428-
let hole_size = self.current().size;
480+
let node_u8 = node.as_ptr().cast::<u8>();
481+
let node_size = unsafe { node.as_ref().size };
429482

430-
// Does hole overlap node?
431-
assert!(
432-
hole_u8.wrapping_add(hole_size) <= node_u8,
433-
"Freed node aliases existing hole! Bad free?",
434-
);
435-
436-
// If we have a next, does the node overlap next?
437-
if let Some(next) = self.current().next.as_ref() {
483+
// If we have a next, does the node overlap next?
484+
if let Some(next) = self.current().next.as_ref() {
485+
if node < *next {
438486
let node_u8 = node_u8 as *const u8;
439487
assert!(
440488
node_u8.wrapping_add(node_size) <= next.as_ptr().cast::<u8>(),
441489
"Freed node aliases existing hole! Bad free?",
442490
);
491+
} else {
492+
// The new hole isn't between current and next.
493+
return Err(());
443494
}
495+
}
444496

445-
// All good! Let's insert that after.
446-
unsafe {
447-
let maybe_next = self.hole.as_mut().next.replace(node);
448-
node.as_mut().next = maybe_next;
449-
}
450-
Ok(())
451-
} else {
452-
Err(())
497+
// At this point, we either have no "next" pointer, or the hole is
498+
// between current and "next". The following assert can only trigger
499+
// if we've gotten our list out of order.
500+
debug_assert!(self.hole < node, "Hole list out of order?");
501+
502+
let hole_u8 = self.hole.as_ptr().cast::<u8>();
503+
let hole_size = self.current().size;
504+
505+
// Does hole overlap node?
506+
assert!(
507+
hole_u8.wrapping_add(hole_size) <= node_u8,
508+
"Freed node aliases existing hole! Bad free?",
509+
);
510+
511+
// All good! Let's insert that after.
512+
unsafe {
513+
let maybe_next = self.hole.as_mut().next.replace(node);
514+
node.as_mut().next = maybe_next;
453515
}
516+
517+
Ok(())
454518
}
455519

456520
// Merge the current node with up to n following nodes
457521
fn try_merge_next_n(self, max: usize) {
458-
let Cursor { prev: _, mut hole } = self;
522+
let Cursor {
523+
prev: _,
524+
mut hole,
525+
top,
526+
..
527+
} = self;
459528

460529
for _ in 0..max {
461530
// Is there a next node?
462531
let mut next = if let Some(next) = unsafe { hole.as_mut() }.next.as_ref() {
463532
*next
464533
} else {
534+
// Since there is no NEXT node, we need to check whether the current
535+
// hole SHOULD extend to the end, but doesn't. This would happen when
536+
// there isn't enough remaining space to place a hole after the current
537+
// node's placement.
538+
check_merge_top(hole, top);
465539
return;
466540
};
467541

@@ -515,7 +589,10 @@ fn deallocate(list: &mut HoleList, addr: *mut u8, size: usize) {
515589
cursor
516590
} else {
517591
// Oh hey, there are no "real" holes at all. That means this just
518-
// becomes the only "real" hole!
592+
// becomes the only "real" hole! Check if this is touching the end
593+
// or the beginning of the allocation range
594+
let hole = check_merge_bottom(hole, list.bottom);
595+
check_merge_top(hole, list.top);
519596
list.first.next = Some(hole);
520597
return;
521598
};
@@ -525,7 +602,7 @@ fn deallocate(list: &mut HoleList, addr: *mut u8, size: usize) {
525602
// previous location the cursor was pointing to.
526603
//
527604
// Otherwise, our cursor will point at the current non-"dummy" head of the list
528-
let (cursor, n) = match cursor.try_insert_back(hole) {
605+
let (cursor, n) = match cursor.try_insert_back(hole, list.bottom) {
529606
Ok(cursor) => {
530607
// Yup! It lives at the front of the list. Hooray! Attempt to merge
531608
// it with just ONE next node, since it is at the front of the list
@@ -578,8 +655,8 @@ pub mod test {
578655
let assumed_location = data.as_mut_ptr().cast();
579656

580657
let heap = Heap::from_slice(data);
581-
assert!(heap.bottom == assumed_location);
582-
assert!(heap.size == HEAP_SIZE);
658+
assert!(heap.bottom() == assumed_location);
659+
assert!(heap.size() == HEAP_SIZE);
583660
heap
584661
}
585662

0 commit comments

Comments
 (0)