I’m trying to make a lean page allocator, and I found myself wanting essentially a virtual.resize
. Linux has mremap
, but Windows and Posix would need to do an allocate → copy → release operation to grow. I see that could cause problems if someone tried to “resize” an uncommited page. So maybe recommit
would be a decent name for it. Is this a bad idea?
Something like:
recommit :: proc "contextless" (old_data: rawptr, old_size, new_size: uint, may_move: bool = true) -> (data: []byte, err: Allocator_Error) {
| return _recommit(old_data, old_size, new_size, may_move)
}
2 Likes
Not necessarily a bad idea. I’ll have to think about it.
2 Likes
Maybe, on second thought, it might be more dubious than it is worth if the win is limited to Linux. I originally thought mremap
was part of posix. Here is what I had in mind for the generic implementation:
_recommit :: proc "contextless" (old_data: rawptr, old_size, new_size: uint, may_move: bool) -> (data: []byte, err: Allocator_Error) {
if new_size == old_size {
return ([^]byte)(old_data)[:old_size], nil
}
if new_size < old_size {
data = ([^]byte)(old_data)[:new_size]
unused := uintptr(&data[0]) + uintptr(new_size)
release(rawptr(unused), old_size - new_size) // decommit?
return
}
if !may_move {
return ([^]byte)(old_data)[:old_size], .Out_Of_Memory
}
data, err = reserve_and_commit(new_size)
if err != nil {
return ([^]byte)(old_data)[:old_size], .Out_Of_Memory
}
mem.copy_non_overlapping(&data[0], old_data, int(old_size))
release(old_data, old_size)
return
}
1 Like
For what it’s worth this is possible to implement on Darwin as well.
if (large_entry) { // check if "addr = ptr + old_size" is already spoken for
return 0; // large pointer already exists in table - extension is not going to work
}
new_size = round_large_page_quanta(new_size);
/*
* Ask for allocation at a specific address, and mark as realloc
* to request coalescing with previous realloc'ed extensions.
*/
err = vm_allocate(mach_task_self(), &addr, new_size - old_size, VM_MAKE_TAG(VM_MEMORY_REALLOC));
if (err != KERN_SUCCESS) {
return 0;
}
SZONE_LOCK(szone);
/* extend existing large entry */
large_entry = large_entry_for_pointer_no_lock(szone, ptr);
if (!large_entry) {
malloc_zone_error(szone->debug_flags, true, "large entry %p reallocated is not properly in table\n", ptr);
SZONE_UNLOCK(szone);
2 Likes