Previously, a wide slab could be freed during this phase which could
trigger the superpage itself to be freed, resulting in iteration across
potentially invalid memory if the superpage was returned to the host and
not placed in the orphanage.
`heap_free_wide_slab` has had its responsibilities reduced to just
freeing the wide slab, and `heap_free_superpage_if_empty_and_unused` has
been added to better clarify what is happening where.
This should be more robust during merges that happen simultaneously with
remote frees.
Previously, we would preserve the cache entry, but there is an edge case
where a slab of a lower index could be remotely freed and missed during
a merge.
- Simplify the malloc-based allocator
- Correct the type signatures on malloc/calloc/realloc
- Make `heap_alloc/heap_free/heap_resize` API consistent across platforms
- Add the test bench for the allocator
- Move old allocator code to the test bench
- Fix `heap_resize` usage in `os2/env_linux.odin` to fit new API
requiring `old_size`
`typeid` used to be a fancy index with extra metadata stored on it. Now it is direct hash of the type.
This is safe to do in practice since any possible collisions are checked at compile time AND the chances of having a 1% collision are around 1 in 600K (see the Birthday Paradox).
Therefore accessing a `^Type_Info` is now a hash table lookup with linear probing. The table is twice the size than necessary so prevent too much probing due to an overly dense hash table.
This affects `runtime.Arena` and `virtual.Arena`, but not currently
`mem.Arena`. These changes allow the last allocation that has been
made to be resized to a larger size by just extending their
allocation in-place, when there's sufficient room in the memory block to
do so.
Shrinking in place and re-using the rest of the allocation can be
supported using almost the same logic, but would require the memory to
be zeroed. Since this would add a additional cost that isn't currently
present, shrinking has not been changed.
`map_desired_position` does not return the actual position, probing must
be done afterwards to figure out the real position. `map_entry` did not
do this for the returned key pointer so it could point to the wrong key
if probing was done.
This provides an equivalent to `assert` and `assert_contextless` that
are always evaluated, ignoring `ODIN_DISABLE_ASSERT`, which is useful
for enforcing API contracts or "asserting" on conditionals with
side-effects.