Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial heap local deferred free implementation #397

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions doc/mimalloc-doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,32 @@ bool mi_heap_check_owned(mi_heap_t* heap, const void* p);
/// @see mi_heap_contains_block()
/// @see mi_heap_get_default()
bool mi_check_owned(const void* p);
/// Type of deferred free functions.
playXE marked this conversation as resolved.
Show resolved Hide resolved
/// @param heap The heap.
/// @param force If \a true all outstanding items should be freed.
/// @param heartbeat A monotonically increasing count.
/// @param arg Argument that was passed at registration to hold extra state.
///
/// @see mi_heap_register_deferred_free
typedef void (mi_local_deferred_free_fun)(mi_heap_t* heap,bool force,unsigned long long heartbeat,void* arg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistent style, a space character should follow the comma. That is,

typedef void (mi_local_deferred_free_fun)(mi_heap_t* heap, bool force, unsigned long long heartbeat, void* arg);


/// Register a deferred free function.
/// @param heap The heap where deferred function should be registered.
/// @param deferred_free Address of a deferred free-ing function or \a NULL to unregister.
/// @param arg Argument that will be passed on to the deferred free function.
///
/// Some runtime systems use deferred free-ing, for example when using
playXE marked this conversation as resolved.
Show resolved Hide resolved
/// reference counting to limit the worst case free time.
/// Such systems can register (re-entrant) deferred free function
/// to free more memory on demand. When the \a force parameter is
/// \a true all possible memory should be freed.
/// The per-thread \a heartbeat parameter is monotonically increasing
/// and guaranteed to be deterministic if the program allocates
/// deterministically. The \a deferred_free function is guaranteed
/// to be called deterministically after some number of allocations
/// (regardless of freeing or available free memory).
/// At most one \a deferred_free function can be active.
void mi_heap_register_deferred_free(mi_heap_t* heap,mi_local_deferred_free_fun* deferred_free, void* arg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto. Take care of the style.


/// An area of heap space contains blocks of a single size.
/// The bytes in freed blocks are `committed - used`.
Expand Down
3 changes: 3 additions & 0 deletions include/mimalloc-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ struct mi_heap_s {
size_t page_retired_max; // largest retired index into the `pages` array.
mi_heap_t* next; // list of heaps per thread
bool no_reclaim; // `true` if this heap should not reclaim abandoned pages

mi_local_deferred_free_fun* deferred_free; // local deferred free function
void* deferred_free_arg; // argument passed to local deferred free function
};


Expand Down
5 changes: 4 additions & 1 deletion include/mimalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,17 @@ mi_decl_nodiscard mi_decl_export void* mi_realloc_aligned_at(void* p, size_t new
struct mi_heap_s;
typedef struct mi_heap_s mi_heap_t;

typedef void (mi_cdecl mi_local_deferred_free_fun)(mi_heap_t* heap,bool force,unsigned long long heartbeat,void* arg);


mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new(void);
mi_decl_export void mi_heap_delete(mi_heap_t* heap);
mi_decl_export void mi_heap_destroy(mi_heap_t* heap);
mi_decl_export mi_heap_t* mi_heap_set_default(mi_heap_t* heap);
mi_decl_export mi_heap_t* mi_heap_get_default(void);
mi_decl_export mi_heap_t* mi_heap_get_backing(void);
mi_decl_export void mi_heap_collect(mi_heap_t* heap, bool force) mi_attr_noexcept;

mi_decl_export void mi_heap_register_deferred_free(mi_heap_t* heap,mi_local_deferred_free_fun* fun,void* arg);
mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2, 3);
Expand Down
8 changes: 8 additions & 0 deletions src/heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ mi_heap_t* mi_heap_new(void) {
// push on the thread local heaps list
heap->next = heap->tld->heaps;
heap->tld->heaps = heap;
heap->deferred_free = NULL;
heap->deferred_free_arg = NULL;
return heap;
}

Expand Down Expand Up @@ -564,3 +566,9 @@ bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_blocks, mi_block_vis
mi_visit_blocks_args_t args = { visit_blocks, visitor, arg };
return mi_heap_visit_areas(heap, &mi_heap_area_visitor, &args);
}


void mi_heap_register_deferred_free(mi_heap_t* heap,mi_local_deferred_free_fun* fun,void*arg) {
heap->deferred_free = fun;
heap->deferred_free_arg = arg;
}
8 changes: 6 additions & 2 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ mi_decl_cache_align const mi_heap_t _mi_heap_empty = {
0, // page count
MI_BIN_FULL, 0, // page retired min/max
NULL, // next
false
false,
NULL,
NULL
};

// the thread-local default heap for allocation
Expand Down Expand Up @@ -130,7 +132,9 @@ mi_heap_t _mi_heap_main = {
0, // page count
MI_BIN_FULL, 0, // page retired min/max
NULL, // next heap
false // can reclaim
false, // can reclaim
NULL,
NULL
};

bool _mi_process_is_initialized = false; // set to `true` in `mi_process_init`.
Expand Down
10 changes: 8 additions & 2 deletions src/page.c
Original file line number Diff line number Diff line change
Expand Up @@ -737,9 +737,15 @@ static _Atomic(void*) deferred_arg; // = NULL

void _mi_deferred_free(mi_heap_t* heap, bool force) {
heap->tld->heartbeat++;
if (deferred_free != NULL && !heap->tld->recurse) {
if (!heap->tld->recurse) {
heap->tld->recurse = true;
deferred_free(force, heap->tld->heartbeat, mi_atomic_load_ptr_relaxed(void,&deferred_arg));
if (deferred_free != NULL) {
deferred_free(force, heap->tld->heartbeat, mi_atomic_load_ptr_relaxed(void,&deferred_arg));
}
if (heap->deferred_free != NULL) {
// TODO: Should heap->deferred_free_arg be an atomic load?
(heap->deferred_free)(heap, force, heap->tld->heartbeat, heap->deferred_free_arg);
playXE marked this conversation as resolved.
Show resolved Hide resolved
}
heap->tld->recurse = false;
}
}
Expand Down