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

runtime page size detection + rework GeneralPurposeAllocator to reduce active mapping count + Allocator VTable API update #20511

Merged
merged 39 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
439667b
runtime page size detection
archbirdplus Oct 20, 2024
284de7d
adjust runtime page size APIs
andrewrk Jan 29, 2025
95a0474
revert GPA to before this branch
andrewrk Jan 30, 2025
4913de3
GeneralPurposeAllocator: minimal fix
andrewrk Jan 30, 2025
5c63884
add std.mem.Alignment API
andrewrk Jan 31, 2025
91f41bd
std.heap.PageAllocator: restore high alignment functionality
andrewrk Jan 31, 2025
b23662f
std.heap.WasmAllocator: use `@splat` syntax
andrewrk Jan 31, 2025
dd2fa4f
std.heap.GeneralPurposeAllocator: runtime-known page size
andrewrk Jan 31, 2025
7eeef5f
std.mem.Allocator: introduce `remap` function to the interface
andrewrk Feb 4, 2025
a0b2a18
std.testing.FailingAllocator: flatten namespace
andrewrk Feb 4, 2025
a4d4e08
introduce std.posix.mremap and use it
andrewrk Feb 4, 2025
0d8166b
std: update to new Allocator API
andrewrk Feb 4, 2025
f171777
std.heap: delete LoggingAllocator and friends
andrewrk Feb 4, 2025
becd168
std.hash_map: placeholder for doc comments
andrewrk Feb 4, 2025
601f632
std.heap.GeneralPurposeAllocator: fix large alloc accounting
andrewrk Feb 4, 2025
2c5113f
std.os.linux.mmap: remove logic that does not belong here
andrewrk Feb 4, 2025
36e9b0f
std.mem.Allocator: keep the undefined memset
andrewrk Feb 4, 2025
1a6d87d
std.heap.ThreadSafeAllocator: update to new Allocator API
andrewrk Feb 5, 2025
8ff7481
std.heap.GeneralPurposeAllocator: inline small allocation metadata
andrewrk Feb 5, 2025
0e0f0c9
std.heap.GeneralPurposeAllocator: check canary in free
andrewrk Feb 5, 2025
82b5a1d
std.heap.GeneralPurposeAllocator: implement resize and remap
andrewrk Feb 5, 2025
00b723d
std.heap.WasmAllocator: update to new Allocator API
andrewrk Feb 5, 2025
b14a350
std.heap.GeneralPurposeAllocator: reduce page size to 512K
andrewrk Feb 5, 2025
c8e807c
std.heap.GeneralPurposeAllocator: use for loops in leak check
andrewrk Feb 5, 2025
def36f2
std.heap.GeneralPurposeAllocator: usize for used_bits
andrewrk Feb 5, 2025
d20d934
std: fix compilation under -lc
andrewrk Feb 5, 2025
7320e8b
std.testing: make some things not pub
andrewrk Feb 5, 2025
8626191
std.heap.GeneralPurposeAllocator: fix slot_counts calculation
andrewrk Feb 5, 2025
6aab1ea
std.heap: update Windows HeapAllocator
andrewrk Feb 6, 2025
8282565
std.heap.GeneralPurposeAllocator: fix UAF in resizeLarge
andrewrk Feb 6, 2025
f82ec3f
std.testing.allocator: different canary + enable resize traces
andrewrk Feb 6, 2025
5e9b8c3
std.heap: remove HeapAllocator
andrewrk Feb 6, 2025
cd99ab3
std.heap: rename GeneralPurposeAllocator to DebugAllocator
andrewrk Feb 6, 2025
9601906
std.heap.DebugAllocator: make page size configurable
andrewrk Feb 6, 2025
d0e1a6a
std.heap.DebugAllocator: update unit tests for new impl
andrewrk Feb 6, 2025
ff8e759
std.testing: don't ask wasm to stack trace
andrewrk Feb 6, 2025
1bb8b4a
std.heap: fix wrong deprecation date
andrewrk Feb 6, 2025
e630b20
std.mem.Allocator.VTable: improve doc comment wording
andrewrk Feb 6, 2025
b8f5cfe
std.heap.SbrkAllocator: fix typo
andrewrk Feb 6, 2025
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
2 changes: 1 addition & 1 deletion lib/fuzzer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ pub const MemoryMappedList = struct {
/// of this ArrayList in accordance with the respective documentation. In
/// all cases, "invalidated" means that the memory has been passed to this
/// allocator's resize or free function.
items: []align(std.mem.page_size) volatile u8,
items: []align(std.heap.page_size_min) volatile u8,
/// How many bytes this list can hold without allocating additional memory.
capacity: usize,

Expand Down
2 changes: 1 addition & 1 deletion lib/std/Build/Fuzz/WebServer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const fuzzer_arch_os_abi = "wasm32-freestanding";
const fuzzer_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";

const CoverageMap = struct {
mapped_memory: []align(std.mem.page_size) const u8,
mapped_memory: []align(std.heap.page_size_min) const u8,
coverage: Coverage,
source_locations: []Coverage.SourceLocation,
/// Elements are indexes into `source_locations` pointing to the unit tests that are being fuzz tested.
Expand Down
6 changes: 3 additions & 3 deletions lib/std/Thread.zig
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ const PosixThreadImpl = struct {
// Use the same set of parameters used by the libc-less impl.
const stack_size = @max(config.stack_size, 16 * 1024);
assert(c.pthread_attr_setstacksize(&attr, stack_size) == .SUCCESS);
assert(c.pthread_attr_setguardsize(&attr, std.mem.page_size) == .SUCCESS);
assert(c.pthread_attr_setguardsize(&attr, std.heap.pageSize()) == .SUCCESS);

var handle: c.pthread_t = undefined;
switch (c.pthread_create(
Expand Down Expand Up @@ -1155,7 +1155,7 @@ const LinuxThreadImpl = struct {
completion: Completion = Completion.init(.running),
child_tid: std.atomic.Value(i32) = std.atomic.Value(i32).init(1),
parent_tid: i32 = undefined,
mapped: []align(std.mem.page_size) u8,
mapped: []align(std.heap.page_size_min) u8,

/// Calls `munmap(mapped.ptr, mapped.len)` then `exit(1)` without touching the stack (which lives in `mapped.ptr`).
/// Ported over from musl libc's pthread detached implementation:
Expand Down Expand Up @@ -1362,7 +1362,7 @@ const LinuxThreadImpl = struct {
};

fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl {
const page_size = std.mem.page_size;
const page_size = std.heap.pageSize();
const Args = @TypeOf(args);
const Instance = struct {
fn_args: Args,
Expand Down
37 changes: 18 additions & 19 deletions lib/std/array_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -105,21 +105,19 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
return result;
}

/// The caller owns the returned memory. Empties this ArrayList,
/// Its capacity is cleared, making deinit() safe but unnecessary to call.
/// The caller owns the returned memory. Empties this ArrayList.
/// Its capacity is cleared, making `deinit` safe but unnecessary to call.
pub fn toOwnedSlice(self: *Self) Allocator.Error!Slice {
const allocator = self.allocator;

const old_memory = self.allocatedSlice();
if (allocator.resize(old_memory, self.items.len)) {
const result = self.items;
if (allocator.remap(old_memory, self.items.len)) |new_items| {
self.* = init(allocator);
return result;
return new_items;
}

const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
@memcpy(new_memory, self.items);
@memset(self.items, undefined);
self.clearAndFree();
return new_memory;
}
Expand Down Expand Up @@ -185,8 +183,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
// extra capacity.
const new_capacity = growCapacity(self.capacity, new_len);
const old_memory = self.allocatedSlice();
if (self.allocator.resize(old_memory, new_capacity)) {
self.capacity = new_capacity;
if (self.allocator.remap(old_memory, new_capacity)) |new_memory| {
self.items.ptr = new_memory.ptr;
self.capacity = new_memory.len;
return addManyAtAssumeCapacity(self, index, count);
}

Expand Down Expand Up @@ -468,8 +467,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
// the allocator implementation would pointlessly copy our
// extra capacity.
const old_memory = self.allocatedSlice();
if (self.allocator.resize(old_memory, new_capacity)) {
self.capacity = new_capacity;
if (self.allocator.remap(old_memory, new_capacity)) |new_memory| {
self.items.ptr = new_memory.ptr;
self.capacity = new_memory.len;
} else {
const new_memory = try self.allocator.alignedAlloc(T, alignment, new_capacity);
@memcpy(new_memory[0..self.items.len], self.items);
Expand Down Expand Up @@ -707,15 +707,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Its capacity is cleared, making deinit() safe but unnecessary to call.
pub fn toOwnedSlice(self: *Self, allocator: Allocator) Allocator.Error!Slice {
const old_memory = self.allocatedSlice();
if (allocator.resize(old_memory, self.items.len)) {
const result = self.items;
if (allocator.remap(old_memory, self.items.len)) |new_items| {
self.* = .empty;
return result;
return new_items;
}

const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
@memcpy(new_memory, self.items);
@memset(self.items, undefined);
self.clearAndFree(allocator);
return new_memory;
}
Expand Down Expand Up @@ -1031,9 +1029,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
}

const old_memory = self.allocatedSlice();
if (allocator.resize(old_memory, new_len)) {
self.capacity = new_len;
self.items.len = new_len;
if (allocator.remap(old_memory, new_len)) |new_items| {
self.capacity = new_items.len;
self.items = new_items;
return;
}

Expand Down Expand Up @@ -1099,8 +1097,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
// the allocator implementation would pointlessly copy our
// extra capacity.
const old_memory = self.allocatedSlice();
if (allocator.resize(old_memory, new_capacity)) {
self.capacity = new_capacity;
if (allocator.remap(old_memory, new_capacity)) |new_memory| {
self.items.ptr = new_memory.ptr;
self.capacity = new_memory.len;
} else {
const new_memory = try allocator.alignedAlloc(T, alignment, new_capacity);
@memcpy(new_memory[0..self.items.len], self.items);
Expand Down
57 changes: 50 additions & 7 deletions lib/std/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const builtin = @import("builtin");
const c = @This();
const maxInt = std.math.maxInt;
const assert = std.debug.assert;
const page_size = std.mem.page_size;
const page_size = std.heap.page_size_min;
const native_abi = builtin.abi;
const native_arch = builtin.cpu.arch;
const native_os = builtin.os.tag;
Expand Down Expand Up @@ -2227,6 +2227,39 @@ pub const SC = switch (native_os) {
.linux => linux.SC,
else => void,
};

pub const _SC = switch (native_os) {
.driverkit, .ios, .macos, .tvos, .visionos, .watchos => enum(c_int) {
PAGESIZE = 29,
},
.dragonfly => enum(c_int) {
PAGESIZE = 47,
},
.freebsd => enum(c_int) {
PAGESIZE = 47,
},
.fuchsia => enum(c_int) {
PAGESIZE = 30,
},
.haiku => enum(c_int) {
PAGESIZE = 27,
},
.linux => enum(c_int) {
PAGESIZE = 30,
},
.netbsd => enum(c_int) {
PAGESIZE = 28,
},
.openbsd => enum(c_int) {
PAGESIZE = 28,
},
.solaris, .illumos => enum(c_int) {
PAGESIZE = 11,
NPROCESSORS_ONLN = 15,
},
else => void,
};

pub const SEEK = switch (native_os) {
.linux => linux.SEEK,
.emscripten => emscripten.SEEK,
Expand Down Expand Up @@ -7834,6 +7867,11 @@ pub const MAP = switch (native_os) {
else => void,
};

pub const MREMAP = switch (native_os) {
.linux => linux.MREMAP,
else => void,
};

/// Used by libc to communicate failure. Not actually part of the underlying syscall.
pub const MAP_FAILED: *anyopaque = @ptrFromInt(maxInt(usize));

Expand Down Expand Up @@ -9232,7 +9270,7 @@ pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
pub extern "c" fn getpwuid(uid: uid_t) ?*passwd;
pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int;
pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64;
pub extern "c" fn mmap64(addr: ?*align(std.mem.page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
pub extern "c" fn open64(path: [*:0]const u8, oflag: O, ...) c_int;
pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: O, ...) c_int;
pub extern "c" fn pread64(fd: fd_t, buf: [*]u8, nbyte: usize, offset: i64) isize;
Expand Down Expand Up @@ -9324,13 +9362,13 @@ pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int;

pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;
pub extern "c" fn mincore(
addr: *align(std.mem.page_size) anyopaque,
addr: *align(page_size) anyopaque,
length: usize,
vec: [*]u8,
) c_int;

pub extern "c" fn madvise(
addr: *align(std.mem.page_size) anyopaque,
addr: *align(page_size) anyopaque,
length: usize,
advice: u32,
) c_int;
Expand Down Expand Up @@ -9428,6 +9466,10 @@ pub const posix_memalign = switch (native_os) {
.dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos => private.posix_memalign,
else => {},
};
pub const sysconf = switch (native_os) {
.solaris => solaris.sysconf,
else => private.sysconf,
};

pub const sf_hdtr = switch (native_os) {
.freebsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct {
Expand Down Expand Up @@ -9471,6 +9513,7 @@ pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;
pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: off_t) isize;
pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: MAP, fd: fd_t, offset: off_t) *anyopaque;
pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;
pub extern "c" fn mremap(addr: ?*align(page_size) const anyopaque, old_len: usize, new_len: usize, flags: MREMAP, ...) *anyopaque;
pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;
pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8) c_int;
pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int;
Expand Down Expand Up @@ -9823,7 +9866,6 @@ pub const SCM = solaris.SCM;
pub const SETCONTEXT = solaris.SETCONTEXT;
pub const SETUSTACK = solaris.GETUSTACK;
pub const SFD = solaris.SFD;
pub const _SC = solaris._SC;
pub const cmsghdr = solaris.cmsghdr;
pub const ctid_t = solaris.ctid_t;
pub const file_obj = solaris.file_obj;
Expand All @@ -9840,7 +9882,6 @@ pub const priority = solaris.priority;
pub const procfs = solaris.procfs;
pub const projid_t = solaris.projid_t;
pub const signalfd_siginfo = solaris.signalfd_siginfo;
pub const sysconf = solaris.sysconf;
pub const taskid_t = solaris.taskid_t;
pub const zoneid_t = solaris.zoneid_t;

Expand Down Expand Up @@ -9997,6 +10038,7 @@ pub const host_t = darwin.host_t;
pub const ipc_space_t = darwin.ipc_space_t;
pub const ipc_space_port_t = darwin.ipc_space_port_t;
pub const kern_return_t = darwin.kern_return_t;
pub const vm_size_t = darwin.vm_size_t;
pub const kevent64 = darwin.kevent64;
pub const kevent64_s = darwin.kevent64_s;
pub const mach_absolute_time = darwin.mach_absolute_time;
Expand Down Expand Up @@ -10168,6 +10210,7 @@ const private = struct {
extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
extern "c" fn sysconf(sc: c_int) c_long;

extern "c" fn pthread_setname_np(thread: pthread_t, name: [*:0]const u8) c_int;
extern "c" fn getcontext(ucp: *ucontext_t) c_int;
Expand Down Expand Up @@ -10202,7 +10245,7 @@ const private = struct {
extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int;
extern "c" fn __gettimeofday50(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
extern "c" fn __libc_thr_yield() c_int;
extern "c" fn __msync13(addr: *align(std.mem.page_size) const anyopaque, len: usize, flags: c_int) c_int;
extern "c" fn __msync13(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int;
extern "c" fn __sigaction14(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
extern "c" fn __sigfillset14(set: ?*sigset_t) void;
Expand Down
4 changes: 0 additions & 4 deletions lib/std/c/solaris.zig
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,6 @@ pub const AF_SUN = struct {
pub const NOPLM = 0x00000004;
};

pub const _SC = struct {
pub const NPROCESSORS_ONLN = 15;
};

pub const procfs = struct {
pub const misc_header = extern struct {
size: u32,
Expand Down
10 changes: 5 additions & 5 deletions lib/std/crypto/tlcsprng.zig
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var install_atfork_handler = std.once(struct {
}
}.do);

threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{};
threadlocal var wipe_mem: []align(std.heap.page_size_min) u8 = &[_]u8{};

fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
if (os_has_arc4random) {
Expand Down Expand Up @@ -77,15 +77,15 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
} else {
// Use a static thread-local buffer.
const S = struct {
threadlocal var buf: Context align(mem.page_size) = .{
threadlocal var buf: Context align(std.heap.page_size_min) = .{
.init_state = .uninitialized,
.rng = undefined,
};
};
wipe_mem = mem.asBytes(&S.buf);
}
}
const ctx = @as(*Context, @ptrCast(wipe_mem.ptr));
const ctx: *Context = @ptrCast(wipe_mem.ptr);

switch (ctx.init_state) {
.uninitialized => {
Expand Down Expand Up @@ -141,7 +141,7 @@ fn childAtForkHandler() callconv(.c) void {
}

fn fillWithCsprng(buffer: []u8) void {
const ctx = @as(*Context, @ptrCast(wipe_mem.ptr));
const ctx: *Context = @ptrCast(wipe_mem.ptr);
return ctx.rng.fill(buffer);
}

Expand All @@ -157,7 +157,7 @@ fn initAndFill(buffer: []u8) void {
// the `std.options.cryptoRandomSeed` function is provided.
std.options.cryptoRandomSeed(&seed);

const ctx = @as(*Context, @ptrCast(wipe_mem.ptr));
const ctx: *Context = @ptrCast(wipe_mem.ptr);
ctx.rng = Rng.init(seed);
std.crypto.secureZero(u8, &seed);

Expand Down
Loading
Loading