diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index c265fd0ff27f..8beba21fbb8f 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -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, @@ -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; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 6d0b88e3c35b..fec06c9111c0 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -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, @@ -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; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 1a3c63451502..60eebddff76a 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -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; @@ -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, diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index fb2aefdb44b2..7522e507bc51 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -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, @@ -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; diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index e97798b0e794..03fc0f8c110e 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -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, }; @@ -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; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index 31900c83728b..2b2cf2544a80 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -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, @@ -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; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 9ff272a3328e..7e4042df7d3e 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -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); } @@ -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; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index fb2262e267bc..a03134735b2a 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -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; @@ -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), diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 1020bef4b7b0..5e07282e9b34 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -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(); +} diff --git a/lib/std/testing.zig b/lib/std/testing.zig index c6a4f9f78f5b..d9d6b94c20d1 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -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.