Fil-C on Nix

Filnix wiki

jemalloc-on-fugc.md

jemalloc on Fil-C's allocator

pkgsFilc.jemalloc is not jemalloc's allocator. It is jemalloc's API (<jemalloc/jemalloc.h>, libjemalloc, jemalloc.pc, jemalloc-config) implemented on Fil-C's garbage-collected heap, the same approach as boehm-on-fugc.md. The code is in ports/jemalloc/, and ports/overlay.nix wires it in.

Why not port jemalloc itself

Upstream jemalloc 5.3.1 nearly runs under Fil-C. Its tests stop in pages_boot on a raw syscall(SYS_open) and in the DSS code on inline brk, but with JEMALLOC_USE_SYSCALL and JEMALLOC_DSS turned off its a0, mallctl and rtree unit tests pass. Running it is the problem:

So nothing jemalloc does for performance survives, and what it costs is the point of Fil-C. Consumers only need the API.

Mapping

jemalloc Implementation
malloc, calloc, realloc, free, posix_memalign, aligned_alloc, memalign, valloc, pvalloc, malloc_usable_size, free_sized, free_aligned_sized not defined; libc's, which forward to zgc_alloc and friends
mallocx zgc_alloc, or zgc_aligned_alloc for MALLOCX_ALIGN above 16. Memory is always zeroed, so MALLOCX_ZERO is free. Size 0 allocates 1 byte, as jemalloc does
rallocx zgc_realloc / zgc_aligned_realloc; growth is zero-filled
xallocx cannot grow in place; returns the current size, as jemalloc does when it cannot
sallocx zgetupper(p) - p, which equals malloc_usable_size
nallocx the size rounded up to 16, which is exactly what zgc_alloc gives (checked for every size below 70000 and sampled to 3 MB)
dallocx, sdallocx zgc_free; later accesses trap
MALLOCX_ARENA, MALLOCX_TCACHE* accepted and ignored
mallctl, mallctlnametomib, mallctlbymib a table of the names below
malloc_stats_print a short report (JSON with J) through the callback, malloc_message or stderr
malloc_conf, malloc_conf_2_conf_harder weak definitions; an application's own definition wins. Options are ignored

mallctl

The table mimics a jemalloc with one arena, no thread caches and no profiling. Unknown names return ENOENT, writes to read-only names EPERM, and a wrong *oldlenp gets jemalloc's partial copy and EINVAL.

A MIB is { table index, 0, ..., numeric component, ... }: numeric components (arena and bin indexes) sit where jemalloc puts them, so callers that patch mib[2] before mallctlbymib work. Partial names are not supported.

Tests

ports/jemalloc/test.c runs in the check phase. It covers sizes and alignments against sallocx, nallocx and malloc_usable_size, zeroing and rallocx contents, the mallctl behaviour above and MIB round trips. It also checks, in forked children, that overflowing into a neighbouring object and using an object after dallocx or sdallocx trap.

Consumers

Of the packages in the nixos-26.05 campaign that were blocked on jemalloc: