mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-06 23:39:31 +03:00
add checks for when memory commit fails to return NULL
This commit is contained in:
parent
e4ddc75069
commit
c1a834e886
1 changed files with 37 additions and 42 deletions
|
@ -196,39 +196,19 @@ static size_t mi_segment_info_size(mi_segment_t* segment) {
|
||||||
return segment->segment_info_slices * MI_SEGMENT_SLICE_SIZE;
|
return segment->segment_info_slices * MI_SEGMENT_SLICE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t* _mi_segment_page_start_from_slice(const mi_segment_t* segment, const mi_slice_t* slice, size_t* page_size)
|
||||||
|
{
|
||||||
|
ptrdiff_t idx = slice - segment->slices;
|
||||||
|
size_t psize = slice->slice_count*MI_SEGMENT_SLICE_SIZE;
|
||||||
|
if (page_size != NULL) *page_size = psize;
|
||||||
|
return (uint8_t*)segment + (idx*MI_SEGMENT_SLICE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
// Start of the page available memory; can be used on uninitialized pages
|
// Start of the page available memory; can be used on uninitialized pages
|
||||||
uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size)
|
uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size)
|
||||||
{
|
{
|
||||||
const mi_slice_t* slice = mi_page_to_slice((mi_page_t*)page);
|
const mi_slice_t* slice = mi_page_to_slice((mi_page_t*)page);
|
||||||
ptrdiff_t idx = slice - segment->slices;
|
uint8_t* p = _mi_segment_page_start_from_slice(segment, slice, page_size);
|
||||||
size_t psize = slice->slice_count*MI_SEGMENT_SLICE_SIZE;
|
|
||||||
uint8_t* p = (uint8_t*)segment + (idx*MI_SEGMENT_SLICE_SIZE);
|
|
||||||
/*
|
|
||||||
if (idx == 0) {
|
|
||||||
// the first page starts after the segment info (and possible guard page)
|
|
||||||
p += segment->segment_info_size;
|
|
||||||
psize -= segment->segment_info_size;
|
|
||||||
|
|
||||||
// for small and medium objects, ensure the page start is aligned with the block size (PR#66 by kickunderscore)
|
|
||||||
// to ensure this, we over-estimate and align with the OS page size
|
|
||||||
const size_t asize = _mi_os_page_size();
|
|
||||||
uint8_t* q = (uint8_t*)_mi_align_up((uintptr_t)p, _mi_os_page_size());
|
|
||||||
if (p < q) {
|
|
||||||
psize -= (q - p);
|
|
||||||
p = q;
|
|
||||||
}
|
|
||||||
mi_assert_internal((uintptr_t)p % _mi_os_page_size() == 0);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/* TODO: guard pages between every slice span
|
|
||||||
if (MI_SECURE > 1 || (MI_SECURE == 1 && slice == &segment->slices[segment->slice_entries - 1])) {
|
|
||||||
// secure == 1: the last page has an os guard page at the end
|
|
||||||
// secure > 1: every page has an os guard page
|
|
||||||
psize -= _mi_os_page_size();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (page_size != NULL) *page_size = psize;
|
|
||||||
mi_assert_internal(page->xblock_size == 0 || _mi_ptr_page(p) == page);
|
mi_assert_internal(page->xblock_size == 0 || _mi_ptr_page(p) == page);
|
||||||
mi_assert_internal(_mi_ptr_segment(p) == segment);
|
mi_assert_internal(_mi_ptr_segment(p) == segment);
|
||||||
return p;
|
return p;
|
||||||
|
@ -394,21 +374,21 @@ static uintptr_t mi_segment_commit_mask(mi_segment_t* segment, bool conservative
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mi_segment_commitx(mi_segment_t* segment, bool commit, uint8_t* p, size_t size, mi_stats_t* stats) {
|
static bool mi_segment_commitx(mi_segment_t* segment, bool commit, uint8_t* p, size_t size, mi_stats_t* stats) {
|
||||||
// commit liberal, but decommit conservative
|
// commit liberal, but decommit conservative
|
||||||
uint8_t* start;
|
uint8_t* start;
|
||||||
size_t full_size;
|
size_t full_size;
|
||||||
uintptr_t mask = mi_segment_commit_mask(segment,!commit/*conservative*/,p,size,&start,&full_size);
|
uintptr_t mask = mi_segment_commit_mask(segment,!commit/*conservative*/,p,size,&start,&full_size);
|
||||||
if (mask==0 || full_size==0) return;
|
if (mask==0 || full_size==0) return true;
|
||||||
|
|
||||||
if (commit && (segment->commit_mask & mask) != mask) {
|
if (commit && (segment->commit_mask & mask) != mask) {
|
||||||
bool is_zero = false;
|
bool is_zero = false;
|
||||||
_mi_os_commit(start,full_size,&is_zero,stats);
|
if (!_mi_os_commit(start,full_size,&is_zero,stats)) return false;
|
||||||
segment->commit_mask |= mask;
|
segment->commit_mask |= mask;
|
||||||
}
|
}
|
||||||
else if (!commit && (segment->commit_mask & mask) != 0) {
|
else if (!commit && (segment->commit_mask & mask) != 0) {
|
||||||
mi_assert_internal((void*)start != (void*)segment);
|
mi_assert_internal((void*)start != (void*)segment);
|
||||||
_mi_os_decommit(start, full_size, stats);
|
_mi_os_decommit(start, full_size, stats); // ok if this fails
|
||||||
segment->commit_mask &= ~mask;
|
segment->commit_mask &= ~mask;
|
||||||
}
|
}
|
||||||
// increase expiration of reusing part of the delayed decommit
|
// increase expiration of reusing part of the delayed decommit
|
||||||
|
@ -418,11 +398,12 @@ static void mi_segment_commitx(mi_segment_t* segment, bool commit, uint8_t* p, s
|
||||||
// always undo delayed decommits
|
// always undo delayed decommits
|
||||||
segment->decommit_mask &= ~mask;
|
segment->decommit_mask &= ~mask;
|
||||||
mi_assert_internal((segment->commit_mask & segment->decommit_mask) == segment->decommit_mask);
|
mi_assert_internal((segment->commit_mask & segment->decommit_mask) == segment->decommit_mask);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mi_segment_ensure_committed(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
|
static bool mi_segment_ensure_committed(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
|
||||||
if (~segment->commit_mask == 0 && segment->decommit_mask==0) return; // fully committed
|
if (~segment->commit_mask == 0 && segment->decommit_mask==0) return true; // fully committed
|
||||||
mi_segment_commitx(segment,true,p,size,stats);
|
return mi_segment_commitx(segment,true,p,size,stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mi_segment_perhaps_decommit(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
|
static void mi_segment_perhaps_decommit(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
|
||||||
|
@ -580,11 +561,18 @@ static void mi_segment_slice_split(mi_segment_t* segment, mi_slice_t* slice, siz
|
||||||
slice->slice_count = (uint32_t)slice_count;
|
slice->slice_count = (uint32_t)slice_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: may still return NULL if committing the memory failed
|
||||||
static mi_page_t* mi_segment_span_allocate(mi_segment_t* segment, size_t slice_index, size_t slice_count, mi_segments_tld_t* tld) {
|
static mi_page_t* mi_segment_span_allocate(mi_segment_t* segment, size_t slice_index, size_t slice_count, mi_segments_tld_t* tld) {
|
||||||
mi_assert_internal(slice_index < segment->slice_entries);
|
mi_assert_internal(slice_index < segment->slice_entries);
|
||||||
mi_slice_t* slice = &segment->slices[slice_index];
|
mi_slice_t* slice = &segment->slices[slice_index];
|
||||||
mi_assert_internal(slice->xblock_size==0 || slice->xblock_size==1);
|
mi_assert_internal(slice->xblock_size==0 || slice->xblock_size==1);
|
||||||
|
|
||||||
|
// commit before changing the slice data
|
||||||
|
if (!mi_segment_ensure_committed(segment, _mi_segment_page_start_from_slice(segment, slice, NULL), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats)) {
|
||||||
|
return NULL; // commit failed!
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the slices to a page
|
||||||
slice->slice_offset = 0;
|
slice->slice_offset = 0;
|
||||||
slice->slice_count = (uint32_t)slice_count;
|
slice->slice_count = (uint32_t)slice_count;
|
||||||
mi_assert_internal(slice->slice_count == slice_count);
|
mi_assert_internal(slice->slice_count == slice_count);
|
||||||
|
@ -612,8 +600,7 @@ static mi_page_t* mi_segment_span_allocate(mi_segment_t* segment, size_t slice_i
|
||||||
last->xblock_size = 1;
|
last->xblock_size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure the memory is committed
|
// and initialize the page
|
||||||
mi_segment_ensure_committed(segment, _mi_page_start(segment,page,NULL), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats);
|
|
||||||
page->is_reset = false;
|
page->is_reset = false;
|
||||||
page->is_committed = true;
|
page->is_committed = true;
|
||||||
segment->used++;
|
segment->used++;
|
||||||
|
@ -635,7 +622,13 @@ static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_segm
|
||||||
mi_segment_slice_split(segment, slice, slice_count, tld);
|
mi_segment_slice_split(segment, slice, slice_count, tld);
|
||||||
}
|
}
|
||||||
mi_assert_internal(slice != NULL && slice->slice_count == slice_count && slice->xblock_size > 0);
|
mi_assert_internal(slice != NULL && slice->slice_count == slice_count && slice->xblock_size > 0);
|
||||||
return mi_segment_span_allocate(segment, mi_slice_index(slice), slice->slice_count, tld);
|
mi_page_t* page = mi_segment_span_allocate(segment, mi_slice_index(slice), slice->slice_count, tld);
|
||||||
|
if (page == NULL) {
|
||||||
|
// commit failed; return NULL but first restore the slice
|
||||||
|
mi_segment_span_free_coalesce(slice, tld);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return page;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sq++;
|
sq++;
|
||||||
|
@ -732,7 +725,8 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_
|
||||||
}
|
}
|
||||||
|
|
||||||
// reserve first slices for segment info
|
// reserve first slices for segment info
|
||||||
mi_segment_span_allocate(segment, 0, info_slices, tld);
|
mi_page_t* page0 = mi_segment_span_allocate(segment, 0, info_slices, tld);
|
||||||
|
mi_assert_internal(page0!=NULL); if (page0==NULL) return NULL; // cannot fail as we always commit in advance
|
||||||
mi_assert_internal(segment->used == 1);
|
mi_assert_internal(segment->used == 1);
|
||||||
segment->used = 0; // don't count our internal slices towards usage
|
segment->used = 0; // don't count our internal slices towards usage
|
||||||
|
|
||||||
|
@ -744,6 +738,7 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_
|
||||||
else {
|
else {
|
||||||
mi_assert_internal(huge_page!=NULL);
|
mi_assert_internal(huge_page!=NULL);
|
||||||
*huge_page = mi_segment_span_allocate(segment, info_slices, segment_slices - info_slices - guard_slices, tld);
|
*huge_page = mi_segment_span_allocate(segment, info_slices, segment_slices - info_slices - guard_slices, tld);
|
||||||
|
mi_assert_internal(*huge_page != NULL); // cannot fail as we commit in advance
|
||||||
}
|
}
|
||||||
|
|
||||||
mi_assert_expensive(mi_segment_is_valid(segment,tld));
|
mi_assert_expensive(mi_segment_is_valid(segment,tld));
|
||||||
|
|
Loading…
Add table
Reference in a new issue