Skip to content

Commit f02a0b3

Browse files
committed
more aggressive reclaim from free for OS blocks
1 parent 1a9cf7b commit f02a0b3

File tree

3 files changed

+35
-23
lines changed

3 files changed

+35
-23
lines changed

src/arena.c

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ bool _mi_arena_segment_clear_abandoned(mi_segment_t* segment )
764764
if (mi_option_is_enabled(mi_option_visit_abandoned)) {
765765
has_lock = mi_lock_try_acquire(&segment->subproc->abandoned_os_lock);
766766
if (!has_lock) {
767-
return false; // failed to acquire the lock, we just give up
767+
return false; // failed to acquire the lock, we just give up
768768
}
769769
}
770770
// abandon it, but we need to still claim it atomically -- we use the thread_id for that.
@@ -777,9 +777,10 @@ bool _mi_arena_segment_clear_abandoned(mi_segment_t* segment )
777777
// and remove from the abandoned os list (if needed)
778778
mi_segment_t* const next = segment->abandoned_os_next;
779779
mi_segment_t* const prev = segment->abandoned_os_prev;
780+
mi_assert_internal((next == NULL && prev == NULL) || mi_option_is_enabled(mi_option_visit_abandoned));
780781
if (prev != NULL) { prev->abandoned_os_next = next; }
781-
else { segment->subproc->abandoned_os_list = next; }
782-
if (next != NULL) { next->abandoned_os_prev = prev; }
782+
else { segment->subproc->abandoned_os_list = next; }
783+
if (next != NULL) { next->abandoned_os_prev = prev; }
783784
segment->abandoned_os_next = NULL;
784785
segment->abandoned_os_prev = NULL;
785786
}
@@ -809,41 +810,48 @@ bool _mi_arena_segment_clear_abandoned(mi_segment_t* segment )
809810
// clears the thread_id.
810811
void _mi_arena_segment_mark_abandoned(mi_segment_t* segment)
811812
{
812-
mi_atomic_store_release(&segment->thread_id, 0);
813813
mi_assert_internal(segment->used == segment->abandoned);
814814
if mi_unlikely(segment->memid.memkind != MI_MEM_ARENA) {
815815
// not in an arena; count it as abandoned and return (these can be reclaimed on a `free`)
816-
mi_atomic_increment_relaxed(&segment->subproc->abandoned_count);
817816
// if abandoned visiting is allowed, we need to take a lock on the abandoned os list to insert it
817+
bool has_lock = false;
818818
if (mi_option_is_enabled(mi_option_visit_abandoned)) {
819-
if (!mi_lock_acquire(&segment->subproc->abandoned_os_lock)) {
819+
has_lock = mi_lock_acquire(&segment->subproc->abandoned_os_lock);
820+
if (!has_lock) {
820821
_mi_error_message(EFAULT, "internal error: failed to acquire the abandoned (os) segment lock to mark abandonment");
821-
}
822-
else {
823-
// push on the front of the list
824-
mi_segment_t* next = segment->subproc->abandoned_os_list;
825-
mi_assert_internal(next == NULL || next->abandoned_os_prev == NULL);
826-
mi_assert_internal(segment->abandoned_os_prev == NULL);
827-
mi_assert_internal(segment->abandoned_os_next == NULL);
828-
if (next != NULL) { next->abandoned_os_prev = segment; }
829-
segment->abandoned_os_prev = NULL;
830-
segment->abandoned_os_next = next;
831-
segment->subproc->abandoned_os_list = segment;
832-
mi_lock_release(&segment->subproc->abandoned_os_lock);
833-
}
822+
}
823+
}
824+
mi_atomic_store_release(&segment->thread_id, 0); // mark as abandoned (inside the lock)
825+
mi_atomic_increment_relaxed(&segment->subproc->abandoned_count);
826+
if (has_lock) {
827+
// push on the front of the list
828+
mi_segment_t* next = segment->subproc->abandoned_os_list;
829+
mi_assert_internal(next == NULL || next->abandoned_os_prev == NULL);
830+
mi_assert_internal(segment->abandoned_os_prev == NULL);
831+
mi_assert_internal(segment->abandoned_os_next == NULL);
832+
if (next != NULL) { next->abandoned_os_prev = segment; }
833+
segment->abandoned_os_prev = NULL;
834+
segment->abandoned_os_next = next;
835+
segment->subproc->abandoned_os_list = segment;
836+
// and release the lock
837+
mi_lock_release(&segment->subproc->abandoned_os_lock);
834838
}
835839
return;
836840
}
841+
837842
// segment is in an arena, mark it in the arena `blocks_abandoned` bitmap
843+
mi_atomic_store_release(&segment->thread_id, 0); // mark as abandoned
838844
size_t arena_idx;
839845
size_t bitmap_idx;
840846
mi_arena_memid_indices(segment->memid, &arena_idx, &bitmap_idx);
841847
mi_assert_internal(arena_idx < MI_MAX_ARENAS);
842848
mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_idx]);
843849
mi_assert_internal(arena != NULL);
844850
const bool was_unmarked = _mi_bitmap_claim(arena->blocks_abandoned, arena->field_count, 1, bitmap_idx, NULL);
845-
if (was_unmarked) { mi_atomic_increment_relaxed(&segment->subproc->abandoned_count); }
846-
mi_assert_internal(was_unmarked);
851+
if (was_unmarked) {
852+
// note: it could be unmarked if someone reclaimed in between the thread_id=0 and the claim
853+
mi_atomic_increment_relaxed(&segment->subproc->abandoned_count);
854+
}
847855
mi_assert_internal(_mi_bitmap_is_claimed(arena->blocks_inuse, arena->field_count, 1, bitmap_idx));
848856
}
849857

src/segment.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,14 +938,18 @@ static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap,
938938
}
939939
}
940940

941+
941942
// attempt to reclaim a particular segment (called from multi threaded free `alloc.c:mi_free_block_mt`)
942943
bool _mi_segment_attempt_reclaim(mi_heap_t* heap, mi_segment_t* segment) {
943944
if (mi_atomic_load_relaxed(&segment->thread_id) != 0) return false; // it is not abandoned
944945
if (segment->subproc != heap->tld->segments.subproc) return false; // only reclaim within the same subprocess
945946
if (!_mi_heap_memid_is_suitable(heap,segment->memid)) return false; // don't reclaim between exclusive and non-exclusive arena's
946947
// don't reclaim more from a `free` call than half the current segments
947948
// this is to prevent a pure free-ing thread to start owning too many segments
948-
if (heap->tld->segments.reclaim_count * 2 > heap->tld->segments.count) return false;
949+
// (but not for out-of-arena segments as that is the main way to be reclaimed for those)
950+
if (segment->memid.memkind == MI_MEM_ARENA && heap->tld->segments.reclaim_count * 2 > heap->tld->segments.count) {
951+
return false;
952+
}
949953
if (_mi_arena_segment_clear_abandoned(segment)) { // atomically unabandon
950954
mi_segment_t* res = mi_segment_reclaim(segment, heap, 0, NULL, &heap->tld->segments);
951955
mi_assert_internal(res == segment);

test/test-stress.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,8 @@ int main(int argc, char** argv) {
306306

307307
#ifndef USE_STD_MALLOC
308308
#ifndef NDEBUG
309-
// mi_collect(true);
310309
mi_debug_show_arenas(true,true,true);
310+
mi_collect(true);
311311
#endif
312312
mi_stats_print(NULL);
313313
#endif

0 commit comments

Comments
 (0)