Skip to content

Implement (m|mun)lockall for Linux and *nix libcs #19203

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 8 additions & 0 deletions lib/std/c/dragonfly.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub extern "c" fn lwp_gettid() c_int;

pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;

pub extern "c" fn mlockall(flags: c_int) c_int;
pub extern "c" fn munlockall() c_int;

pub const pthread_attr_t = extern struct { // copied from freebsd
__size: [56]u8,
__align: c_long,
Expand Down Expand Up @@ -937,6 +940,11 @@ pub const MADV = struct {
pub const SETMAP = 11;
};

pub const MCL = struct {
pub const CURRENT = 0x01;
pub const FUTURE = 0x02;
};

pub const LOCK = struct {
pub const SH = 1;
pub const EX = 2;
Expand Down
8 changes: 8 additions & 0 deletions lib/std/c/freebsd.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub extern "c" fn getpid() pid_t;

pub extern "c" fn kinfo_getfile(pid: pid_t, cntp: *c_int) ?[*]kinfo_file;

pub extern "c" fn mlockall(flags: c_int) c_int;
pub extern "c" fn munlockall() c_int;

pub const sf_hdtr = extern struct {
headers: [*]const iovec_const,
hdr_cnt: c_int,
Expand Down Expand Up @@ -606,6 +609,11 @@ pub const MADV = struct {
pub const PROTECT = 10;
};

pub const MCL = struct {
pub const CURRENT = 0x01;
pub const FUTURE = 0x02;
};

pub const MSF = struct {
pub const ASYNC = 1;
pub const INVALIDATE = 2;
Expand Down
4 changes: 4 additions & 0 deletions lib/std/c/linux.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub const IOV_MAX = linux.IOV_MAX;
pub const IPPROTO = linux.IPPROTO;
pub const LOCK = linux.LOCK;
pub const MADV = linux.MADV;
pub const MCL = linux.MCL;
pub const MSF = linux.MSF;
pub const MMAP2_UNIT = linux.MMAP2_UNIT;
pub const MSG = linux.MSG;
Expand Down Expand Up @@ -306,6 +307,9 @@ pub extern "c" fn madvise(
advice: c_uint,
) c_int;

pub extern "c" fn mlockall(flags: c_int) c_int;
pub extern "c" fn munlockall() c_int;

pub const pthread_attr_t = extern struct {
__size: [56]u8,
__align: c_long,
Expand Down
8 changes: 8 additions & 0 deletions lib/std/c/netbsd.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub const sigaltstack = __sigaltstack14;

pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;

pub extern "c" fn mlockall(flags: c_int) c_int;
pub extern "c" fn munlockall() c_int;

pub const pthread_spin_t = switch (builtin.cpu.arch) {
.aarch64, .aarch64_be, .aarch64_32 => u8,
.mips, .mipsel, .mips64, .mips64el => u32,
Expand Down Expand Up @@ -500,6 +503,11 @@ pub const CLOCK = struct {
pub const PROCESS_CPUTIME_ID = 0x40000000;
};

pub const MCL = struct {
pub const CURRENT = 0x01;
pub const FUTURE = 0x02;
};

pub const MSF = struct {
pub const ASYNC = 1;
pub const INVALIDATE = 2;
Expand Down
8 changes: 8 additions & 0 deletions lib/std/c/openbsd.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: std.c.O) c_int;
pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) c_int;
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;

pub extern "c" fn mlockall(flags: c_int) c_int;
pub extern "c" fn munlockall() c_int;

pub const pthread_spinlock_t = extern struct {
inner: ?*anyopaque = null,
};
Expand Down Expand Up @@ -425,6 +428,11 @@ pub const CLOCK = struct {
pub const THREAD_CPUTIME_ID = 4;
};

pub const MCL = struct {
pub const CURRENT = 0x01;
pub const FUTURE = 0x02;
};

pub const MSF = struct {
pub const ASYNC = 1;
pub const INVALIDATE = 2;
Expand Down
7 changes: 7 additions & 0 deletions lib/std/c/solaris.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: u
pub extern "c" fn sysconf(sc: c_int) i64;
pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int;
pub extern "c" fn madvise(address: [*]u8, len: usize, advise: u32) c_int;
pub extern "c" fn mlockall(flags: c_int) c_int;
pub extern "c" fn munlockall() c_int;

pub const pthread_attr_t = extern struct {
mutexattr: ?*anyopaque = null,
Expand Down Expand Up @@ -525,6 +527,11 @@ pub const MADV = struct {
pub const PURGE = 9;
};

pub const MCL = struct {
pub const CURRENT = 0x01;
pub const FUTURE = 0x02;
};

pub const W = struct {
pub const EXITED = 0o001;
pub const TRAPPED = 0o002;
Expand Down
18 changes: 18 additions & 0 deletions lib/std/os/linux.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2093,6 +2093,14 @@ pub fn madvise(address: [*]u8, len: usize, advice: u32) usize {
return syscall3(.madvise, @intFromPtr(address), len, advice);
}

pub fn mlockall(flags: i32) usize {
return syscall1(.mlockall, @as(usize, @bitCast(@as(isize, flags))));
}

pub fn munlockall() usize {
return syscall0(.munlockall);
}

pub fn pidfd_open(pid: pid_t, flags: u32) usize {
return syscall2(.pidfd_open, @as(usize, @bitCast(@as(isize, pid))), flags);
}
Expand Down Expand Up @@ -6051,6 +6059,16 @@ pub const MADV = struct {
pub const SOFT_OFFLINE = 101;
};

pub const MCL = if (is_ppc or is_ppc64 or is_sparc) struct {
pub const CURRENT = 0x2000;
pub const FUTURE = 0x4000;
pub const ONFAULT = 0x8000;
} else struct {
pub const CURRENT = 0x01;
pub const FUTURE = 0x02;
pub const ONFAULT = 0x04;
};

pub const POSIX_FADV = switch (native_arch) {
.s390x => if (@typeInfo(usize).Int.bits == 64) struct {
pub const NORMAL = 0;
Expand Down
42 changes: 42 additions & 0 deletions lib/std/posix.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub const Kevent = system.Kevent;
pub const LOCK = system.LOCK;
pub const MADV = system.MADV;
pub const MAP = system.MAP;
pub const MCL = system.MCL;
pub const MSF = system.MSF;
pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
pub const MFD = system.MFD;
Expand Down Expand Up @@ -7075,6 +7076,47 @@ pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) Madv
}
}

pub const MlockallError = error{
InvalidArgument,
LockedMemoryLimitExceeded,
NotImplemented,
PermissionDenied,
SystemResources,
} || UnexpectedError;

pub fn mlockall(flags: i32) MlockallError!void {
switch (native_os) {
.linux, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris => {},
else => return error.NotImplemented,
}
switch (errno(system.mlockall(flags))) {
.SUCCESS => return,
.INVAL => return error.InvalidArgument,
.NOMEM => return error.LockedMemoryLimitExceeded,
.PERM => return error.PermissionDenied,
// Solaris and (Free|Net|Open)BSD, presumably no room to fault in locked stuff:
.AGAIN => return error.SystemResources,
else => |err| return unexpectedErrno(err),
}
}

pub const MunlockallError = error{
NotImplemented,
PermissionDenied,
} || UnexpectedError;

pub fn munlockall() MunlockallError!void {
switch (native_os) {
.linux, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris => {},
else => return error.NotImplemented,
}
switch (errno(system.munlockall())) {
.SUCCESS => return,
.PERM => return error.PermissionDenied, // Solaris, possibly BSDs as well
else => |err| return unexpectedErrno(err),
}
}

pub const PerfEventOpenError = error{
/// Returned if the perf_event_attr size value is too small (smaller
/// than PERF_ATTR_SIZE_VER0), too big (larger than the page size),
Expand Down
19 changes: 19 additions & 0 deletions lib/std/posix/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1315,3 +1315,22 @@ const CommonOpenFlags = packed struct {
return result;
}
};

test "mlockall/munlockall basic smoke" {
// We can't test a "real" mlockall because of various system perms/limits
// on locked memory and/or possible adverse effects on the testing
// environment itself. What we can do safely, I think, is execute
// mlockall() with a very-invalid (across all known platforms) flags value
// and expect E.INVAL (or NotImplemented when testing on non-supporting
// platforms), so that we at least exercise the code paths here.
const ok_errors = [_]anyerror{ error.NotImplemented, error.InvalidArgument };
try testing.expectErrors(&ok_errors, posix.mlockall(0b0001111111111000));

// On Linux, munlockall seems to have no perms or limits issues, so it should
// always be successful. This at least lets us exercise the code in the
// posix wrapper on one platform to know it's not horribly broken. This
// could have a real effect on the testing environment if it were already
// holding locked memory, but it shouldn't be.
if (native_os == .linux)
try posix.munlockall();
}
20 changes: 20 additions & 0 deletions lib/std/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@ pub fn expectError(expected_error: anyerror, actual_error_union: anytype) !void
}
}

/// This function is intended to be used only in tests. It prints diagnostics to stderr
/// and then returns a test failure error when actual_error_union is not one of the
/// errors in expected_errors.
pub fn expectErrors(expected_errors: []const anyerror, actual_error_union: anytype) !void
{
if (actual_error_union) |actual_payload| {
print("expected one of {any}, found {any}\n", .{ expected_errors, actual_payload });
return error.TestUnexpectedError;
} else |actual_error| {
for (expected_errors) |e|
if (actual_error == e)
return;
print("expected one of {any}, found error.{s}\n", .{
expected_errors,
@errorName(actual_error),
});
return error.TestExpectedError;
}
}

/// This function is intended to be used only in tests. When the two values are not
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then returns a test failure error.
Expand Down