aboutsummaryrefslogtreecommitdiff
path: root/std/os
diff options
context:
space:
mode:
authortgschultz <tgschultz@gmail.com>2018-05-11 21:36:02 -0500
committerGitHub <noreply@github.com>2018-05-11 21:36:02 -0500
commit8c1872543c8cf76215cc4bf3ced4637bb1065a4e (patch)
tree72dfebb643ab61579e3fb8dd58cd4610ffe876fa /std/os
parent7186e92c86982950d0aa7c0c2deef9ef96bc1264 (diff)
parent6e821078f625a03eb8b7794c983da0f7793366ab (diff)
downloadzig-8c1872543c8cf76215cc4bf3ced4637bb1065a4e.tar.gz
zig-8c1872543c8cf76215cc4bf3ced4637bb1065a4e.zip
Merge pull request #1 from zig-lang/master
Sync with zig-lang/zig master
Diffstat (limited to 'std/os')
-rw-r--r--std/os/darwin.zig30
-rw-r--r--std/os/epoch.zig26
-rw-r--r--std/os/file.zig45
-rw-r--r--std/os/index.zig1104
-rw-r--r--std/os/linux/i386.zig505
-rw-r--r--std/os/linux/index.zig689
-rw-r--r--std/os/linux/test.zig1
-rw-r--r--std/os/linux/vdso.zig89
-rw-r--r--std/os/linux/x86_64.zig21
-rw-r--r--std/os/test.zig44
-rw-r--r--std/os/time.zig288
-rw-r--r--std/os/windows/index.zig19
12 files changed, 2112 insertions, 749 deletions
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index f8b1fbed3b..0a62b03ab2 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64
pub const O_LARGEFILE = 0x0000;
pub const O_PATH = 0x0000;
+pub const F_OK = 0;
+pub const X_OK = 1;
+pub const W_OK = 2;
+pub const R_OK = 4;
+
pub const O_RDONLY = 0x0000; /// open for reading only
pub const O_WRONLY = 0x0001; /// open for writing only
pub const O_RDWR = 0x0002; /// open for reading and writing
@@ -179,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {
return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
}
-pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
+pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32,
offset: isize) usize
{
const ptr_result = c.mmap(@ptrCast(&c_void, address), length,
@@ -188,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
return errnoWrap(isize_result);
}
-pub fn munmap(address: &u8, length: usize) usize {
- return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));
+pub fn munmap(address: usize, length: usize) usize {
+ return errnoWrap(c.munmap(@intToPtr(&c_void, address), length));
}
pub fn unlink(path: &const u8) usize {
@@ -209,6 +214,10 @@ pub fn fork() usize {
return errnoWrap(c.fork());
}
+pub fn access(path: &const u8, mode: u32) usize {
+ return errnoWrap(c.access(path, mode));
+}
+
pub fn pipe(fds: &[2]i32) usize {
comptime assert(i32.bit_count == c_int.bit_count);
return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
@@ -251,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u
return errnoWrap(c.readlink(path, buf_ptr, buf_len));
}
+pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize {
+ return errnoWrap(c.gettimeofday(tv, tz));
+}
+
pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {
return errnoWrap(c.nanosleep(req, rem));
}
@@ -301,6 +314,9 @@ pub const timespec = c.timespec;
pub const Stat = c.Stat;
pub const dirent = c.dirent;
+pub const sa_family_t = c.sa_family_t;
+pub const sockaddr = c.sockaddr;
+
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
pub const Sigaction = struct {
handler: extern fn(i32)void,
@@ -318,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void {
fn errnoWrap(value: isize) usize {
return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);
}
+
+
+pub const timezone = c.timezone;
+pub const timeval = c.timeval;
+pub const mach_timebase_info_data = c.mach_timebase_info_data;
+
+pub const mach_absolute_time = c.mach_absolute_time;
+pub const mach_timebase_info = c.mach_timebase_info;
diff --git a/std/os/epoch.zig b/std/os/epoch.zig
new file mode 100644
index 0000000000..e1256c1374
--- /dev/null
+++ b/std/os/epoch.zig
@@ -0,0 +1,26 @@
+/// Epoch reference times in terms of their difference from
+/// posix epoch in seconds.
+pub const posix = 0; //Jan 01, 1970 AD
+pub const dos = 315532800; //Jan 01, 1980 AD
+pub const ios = 978307200; //Jan 01, 2001 AD
+pub const openvms = -3506716800; //Nov 17, 1858 AD
+pub const zos = -2208988800; //Jan 01, 1900 AD
+pub const windows = -11644473600; //Jan 01, 1601 AD
+pub const amiga = 252460800; //Jan 01, 1978 AD
+pub const pickos = -63244800; //Dec 31, 1967 AD
+pub const gps = 315964800; //Jan 06, 1980 AD
+pub const clr = -62135769600; //Jan 01, 0001 AD
+
+pub const unix = posix;
+pub const android = posix;
+pub const os2 = dos;
+pub const bios = dos;
+pub const vfat = dos;
+pub const ntfs = windows;
+pub const ntp = zos;
+pub const jbase = pickos;
+pub const aros = amiga;
+pub const morphos = amiga;
+pub const brew = gps;
+pub const atsc = gps;
+pub const go = clr; \ No newline at end of file
diff --git a/std/os/file.zig b/std/os/file.zig
index eed3a443b9..61fc2b1455 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -85,6 +85,47 @@ pub const File = struct {
};
}
+ pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
+ const path_with_null = try std.cstr.addNullByte(allocator, path);
+ defer allocator.free(path_with_null);
+
+ if (is_posix) {
+ // mode is ignored and is always F_OK for now
+ const result = posix.access(path_with_null.ptr, posix.F_OK);
+ const err = posix.getErrno(result);
+ if (err > 0) {
+ return switch (err) {
+ posix.EACCES => error.PermissionDenied,
+ posix.EROFS => error.PermissionDenied,
+ posix.ELOOP => error.PermissionDenied,
+ posix.ETXTBSY => error.PermissionDenied,
+ posix.ENOTDIR => error.NotFound,
+ posix.ENOENT => error.NotFound,
+
+ posix.ENAMETOOLONG => error.NameTooLong,
+ posix.EINVAL => error.BadMode,
+ posix.EFAULT => error.BadPathName,
+ posix.EIO => error.Io,
+ posix.ENOMEM => error.SystemResources,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+ return true;
+ } else if (is_windows) {
+ if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {
+ return true;
+ }
+
+ const err = windows.GetLastError();
+ return switch (err) {
+ windows.ERROR.FILE_NOT_FOUND => error.NotFound,
+ windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
+ else => os.unexpectedErrorWindows(err),
+ };
+ } else {
+ @compileError("TODO implement access for this OS");
+ }
+ }
/// Upon success, the stream is in an uninitialized state. To continue using it,
/// you must use the open() function.
@@ -245,7 +286,9 @@ pub const File = struct {
};
}
- return stat.mode;
+ // TODO: we should be able to cast u16 to ModeError!u32, making this
+ // explicit cast not necessary
+ return os.FileMode(stat.mode);
} else if (is_windows) {
return {};
} else {
diff --git a/std/os/index.zig b/std/os/index.zig
index 4b74af035e..93c5f70f1e 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2,22 +2,42 @@ const std = @import("../index.zig");
const builtin = @import("builtin");
const Os = builtin.Os;
const is_windows = builtin.os == Os.windows;
+const is_posix = switch (builtin.os) {
+ builtin.Os.linux,
+ builtin.Os.macosx => true,
+ else => false,
+};
const os = this;
+test "std.os" {
+ _ = @import("child_process.zig");
+ _ = @import("darwin.zig");
+ _ = @import("darwin_errno.zig");
+ _ = @import("get_user_id.zig");
+ _ = @import("linux/index.zig");
+ _ = @import("path.zig");
+ _ = @import("test.zig");
+ _ = @import("time.zig");
+ _ = @import("windows/index.zig");
+}
+
pub const windows = @import("windows/index.zig");
pub const darwin = @import("darwin.zig");
pub const linux = @import("linux/index.zig");
pub const zen = @import("zen.zig");
-pub const posix = switch(builtin.os) {
+pub const posix = switch (builtin.os) {
Os.linux => linux,
- Os.macosx, Os.ios => darwin,
+ Os.macosx,
+ Os.ios => darwin,
Os.zen => zen,
else => @compileError("Unsupported OS"),
};
+pub const net = @import("net.zig");
pub const ChildProcess = @import("child_process.zig").ChildProcess;
pub const path = @import("path.zig");
pub const File = @import("file.zig").File;
+pub const time = @import("time.zig");
pub const FileMode = switch (builtin.os) {
Os.windows => void,
@@ -40,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite;
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
pub const windowsOpen = windows_util.windowsOpen;
pub const windowsLoadDll = windows_util.windowsLoadDll;
-pub const windowsUnloadDll = windows_util.windowsUnloadDll;
+pub const windowsUnloadDll = windows_util.windowsUnloadDll;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
pub const WindowsWaitError = windows_util.WaitError;
@@ -79,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void {
switch (err) {
posix.EINVAL => unreachable,
posix.EFAULT => unreachable,
- posix.EINTR => continue,
+ posix.EINTR => continue,
posix.ENOSYS => {
- const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);
+ const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
defer close(fd);
try posixRead(fd, buf);
@@ -92,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void {
}
return;
},
- Os.macosx, Os.ios => {
- const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);
+ Os.macosx,
+ Os.ios => {
+ const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
defer close(fd);
try posixRead(fd, buf);
@@ -116,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void {
}
},
Os.zen => {
- const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45};
+ const randomness = []u8 {
+ 42,
+ 1,
+ 7,
+ 12,
+ 22,
+ 17,
+ 99,
+ 16,
+ 26,
+ 87,
+ 41,
+ 45,
+ };
var i: usize = 0;
while (i < buf.len) : (i += 1) {
if (i > randomness.len) return error.Unknown;
@@ -141,7 +175,9 @@ pub fn abort() noreturn {
c.abort();
}
switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
+ Os.linux,
+ Os.macosx,
+ Os.ios => {
_ = posix.raise(posix.SIGABRT);
_ = posix.raise(posix.SIGKILL);
while (true) {}
@@ -163,7 +199,9 @@ pub fn exit(status: u8) noreturn {
c.exit(status);
}
switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
+ Os.linux,
+ Os.macosx,
+ Os.ios => {
posix.exit(status);
},
Os.windows => {
@@ -173,6 +211,13 @@ pub fn exit(status: u8) noreturn {
}
}
+/// When a file descriptor is closed on linux, it pops the first
+/// node from this queue and resumes it.
+/// Async functions which get the EMFILE error code can suspend,
+/// putting their coroutine handle into this list.
+/// TODO make this an atomic linked list
+pub var emfile_promise_queue = std.LinkedList(promise).init();
+
/// Closes the file handle. Keeps trying if it gets interrupted by a signal.
pub fn close(handle: FileHandle) void {
if (is_windows) {
@@ -180,10 +225,12 @@ pub fn close(handle: FileHandle) void {
} else {
while (true) {
const err = posix.getErrno(posix.close(handle));
- if (err == posix.EINTR) {
- continue;
- } else {
- return;
+ switch (err) {
+ posix.EINTR => continue,
+ else => {
+ if (emfile_promise_queue.popFirst()) |p| resume p.data;
+ return;
+ },
}
}
}
@@ -203,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
if (err > 0) {
return switch (err) {
posix.EINTR => continue,
- posix.EINVAL, posix.EFAULT => unreachable,
+ posix.EINVAL,
+ posix.EFAULT => unreachable,
posix.EAGAIN => error.WouldBlock,
posix.EBADF => error.FileClosed,
posix.EIO => error.InputOutput,
posix.EISDIR => error.IsDir,
- posix.ENOBUFS, posix.ENOMEM => error.SystemResources,
+ posix.ENOBUFS,
+ posix.ENOMEM => error.SystemResources,
else => unexpectedErrorPosix(err),
};
}
@@ -242,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
const write_err = posix.getErrno(rc);
if (write_err > 0) {
return switch (write_err) {
- posix.EINTR => continue,
- posix.EINVAL, posix.EFAULT => unreachable,
+ posix.EINTR => continue,
+ posix.EINVAL,
+ posix.EFAULT => unreachable,
posix.EAGAIN => PosixWriteError.WouldBlock,
posix.EBADF => PosixWriteError.FileClosed,
posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
posix.EDQUOT => PosixWriteError.DiskQuota,
- posix.EFBIG => PosixWriteError.FileTooBig,
- posix.EIO => PosixWriteError.InputOutput,
+ posix.EFBIG => PosixWriteError.FileTooBig,
+ posix.EIO => PosixWriteError.InputOutput,
posix.ENOSPC => PosixWriteError.NoSpaceLeft,
- posix.EPERM => PosixWriteError.AccessDenied,
- posix.EPIPE => PosixWriteError.BrokenPipe,
- else => unexpectedErrorPosix(write_err),
+ posix.EPERM => PosixWriteError.AccessDenied,
+ posix.EPIPE => PosixWriteError.BrokenPipe,
+ else => unexpectedErrorPosix(write_err),
};
}
index += rc;
@@ -299,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {
posix.EFAULT => unreachable,
posix.EINVAL => unreachable,
posix.EACCES => return PosixOpenError.AccessDenied,
- posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,
+ posix.EFBIG,
+ posix.EOVERFLOW => return PosixOpenError.FileTooBig,
posix.EISDIR => return PosixOpenError.IsDir,
posix.ELOOP => return PosixOpenError.SymLinkLoop,
posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
@@ -324,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
const err = posix.getErrno(posix.dup2(old_fd, new_fd));
if (err > 0) {
return switch (err) {
- posix.EBUSY, posix.EINTR => continue,
+ posix.EBUSY,
+ posix.EINTR => continue,
posix.EMFILE => error.ProcessFdQuotaExceeded,
posix.EINVAL => unreachable,
else => unexpectedErrorPosix(err),
@@ -359,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap)
pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
for (envp_buf) |env| {
- const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break;
+ const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break;
allocator.free(env_buf);
}
allocator.free(envp_buf);
@@ -370,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
/// pointers after the args and after the environment variables.
/// `argv[0]` is the executable path.
/// This function also uses the PATH environment variable to get the full path to the executable.
-pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
- allocator: &Allocator) !void
-{
+pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void {
const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
mem.set(?&u8, argv_buf, null);
defer {
@@ -411,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
while (it.next()) |search_path| {
mem.copy(u8, path_buf, search_path);
path_buf[search_path.len] = '/';
- mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path);
+ mem.copy(u8, path_buf[search_path.len + 1..], exe_path);
path_buf[search_path.len + exe_path.len + 1] = 0;
err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
assert(err > 0);
@@ -443,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
assert(err > 0);
return switch (err) {
posix.EFAULT => unreachable,
- posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources,
- posix.EACCES, posix.EPERM => error.AccessDenied,
- posix.EINVAL, posix.ENOEXEC => error.InvalidExe,
- posix.EIO, posix.ELOOP => error.FileSystem,
+ posix.E2BIG,
+ posix.EMFILE,
+ posix.ENAMETOOLONG,
+ posix.ENFILE,
+ posix.ENOMEM => error.SystemResources,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
+ posix.EINVAL,
+ posix.ENOEXEC => error.InvalidExe,
+ posix.EIO,
+ posix.ELOOP => error.FileSystem,
posix.EISDIR => error.IsDir,
posix.ENOENT => error.FileNotFound,
posix.ENOTDIR => error.NotDir,
@@ -455,6 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
};
}
+pub var linux_aux_raw = []usize {0} ** 38;
pub var posix_environ_raw: []&u8 = undefined;
/// Caller must free result when done.
@@ -468,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap {
var i: usize = 0;
while (true) {
- if (ptr[i] == 0)
- return result;
+ if (ptr[i] == 0) return result;
const key_start = i;
@@ -507,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const this_key = ptr[0..line_i];
- if (!mem.eql(u8, key, this_key))
- continue;
+ if (!mem.eql(u8, key, this_key)) continue;
var end_i: usize = line_i;
while (ptr[end_i] != 0) : (end_i += 1) {}
@@ -661,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EFAULT, posix.EINVAL => unreachable,
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EDQUOT => error.DiskQuota,
posix.EEXIST => error.PathAlreadyExists,
posix.EIO => error.FileSystem,
@@ -679,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
}
// here we replace the standard +/ with -_ so that it can be used in a file name
-const b64_fs_encoder = base64.Base64Encoder.init(
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
- base64.standard_pad_char);
+const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {
if (symLink(allocator, existing_path, new_path)) {
@@ -700,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
tmp_path[dirname.len] = os.path.sep;
while (true) {
try getRandomBytes(rand_buf[0..]);
- b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
+ b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
if (symLink(allocator, existing_path, tmp_path)) {
return rename(allocator, tmp_path, new_path);
@@ -709,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
else => return err, // TODO zig should know this set does not include PathAlreadyExists
}
}
-
}
pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
@@ -732,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
return switch (err) {
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
windows.ERROR.ACCESS_DENIED => error.AccessDenied,
- windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
+ windows.ERROR.FILENAME_EXCED_RANGE,
+ windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
else => unexpectedErrorWindows(err),
};
}
@@ -748,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
const err = posix.getErrno(posix.unlink(buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.EIO => error.FileSystem,
posix.EISDIR => error.IsDir,
posix.ELOOP => error.SymLinkLoop,
@@ -828,7 +886,7 @@ pub const AtomicFile = struct {
while (true) {
try getRandomBytes(rand_buf[0..]);
- b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
+ b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
error.PathAlreadyExists => continue,
@@ -879,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
new_buf[new_path.len] = 0;
if (is_windows) {
- const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH;
+ const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
const err = windows.GetLastError();
return switch (err) {
@@ -890,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
posix.EDQUOT => error.DiskQuota,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.EISDIR => error.IsDir,
posix.ELOOP => error.SymLinkLoop,
posix.EMLINK => error.LinkQuotaExceeded,
@@ -902,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
posix.ENOTDIR => error.NotDir,
posix.ENOMEM => error.SystemResources,
posix.ENOSPC => error.NoSpaceLeft,
- posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,
+ posix.EEXIST,
+ posix.ENOTEMPTY => error.PathAlreadyExists,
posix.EROFS => error.ReadOnlyFileSystem,
posix.EXDEV => error.RenameAcrossMountPoints,
else => unexpectedErrorPosix(err),
@@ -940,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EDQUOT => error.DiskQuota,
posix.EEXIST => error.PathAlreadyExists,
posix.EFAULT => unreachable,
@@ -970,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {
// TODO stat the file and return an error if it's not a directory
// this is important because otherwise a dangling symlink
// could cause an infinite loop
- if (end_index == resolved_path.len)
- return;
+ if (end_index == resolved_path.len) return;
} else if (err == error.FileNotFound) {
// march end_index backward until next path component
while (true) {
end_index -= 1;
- if (os.path.isSep(resolved_path[end_index]))
- break;
+ if (os.path.isSep(resolved_path[end_index])) break;
}
continue;
} else {
return err;
}
};
- if (end_index == resolved_path.len)
- return;
+ if (end_index == resolved_path.len) return;
// march end_index forward until next path component
while (true) {
end_index += 1;
- if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index]))
- break;
+ if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break;
}
}
}
@@ -1007,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
const err = posix.getErrno(posix.rmdir(path_buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.ELOOP => error.SymLinkLoop,
posix.ENAMETOOLONG => error.NameTooLong,
posix.ENOENT => error.FileNotFound,
posix.ENOMEM => error.SystemResources,
posix.ENOTDIR => error.NotDir,
- posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
+ posix.EEXIST,
+ posix.ENOTEMPTY => error.DirNotEmpty,
posix.EROFS => error.ReadOnlyFileSystem,
else => unexpectedErrorPosix(err),
};
@@ -1025,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
/// Whether ::full_path describes a symlink, file, or directory, this function
/// removes it. If it cannot be removed because it is a non-empty directory,
/// this function recursively removes its entries and then tries again.
-// TODO non-recursive implementation
+/// TODO non-recursive implementation
const DeleteTreeError = error {
OutOfMemory,
AccessDenied,
@@ -1067,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
error.NotDir,
error.FileSystem,
error.FileBusy,
- error.Unexpected
- => return err,
+ error.Unexpected => return err,
}
{
var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
@@ -1092,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
error.SystemResources,
error.NoSpaceLeft,
error.PathAlreadyExists,
- error.Unexpected
- => return err,
+ error.Unexpected => return err,
};
defer dir.close();
@@ -1123,7 +1182,8 @@ pub const Dir = struct {
end_index: usize,
const darwin_seek_t = switch (builtin.os) {
- Os.macosx, Os.ios => i64,
+ Os.macosx,
+ Os.ios => i64,
else => void,
};
@@ -1147,12 +1207,14 @@ pub const Dir = struct {
pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
const fd = switch (builtin.os) {
Os.windows => @compileError("TODO support Dir.open for windows"),
- Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
- Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
+ Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
+ Os.macosx,
+ Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
else => @compileError("Dir.open is not supported for this platform"),
};
const darwin_seek_init = switch (builtin.os) {
- Os.macosx, Os.ios => 0,
+ Os.macosx,
+ Os.ios => 0,
else => {},
};
return Dir {
@@ -1175,7 +1237,8 @@ pub const Dir = struct {
pub fn next(self: &Dir) !?Entry {
switch (builtin.os) {
Os.linux => return self.nextLinux(),
- Os.macosx, Os.ios => return self.nextDarwin(),
+ Os.macosx,
+ Os.ios => return self.nextDarwin(),
Os.windows => return self.nextWindows(),
else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
}
@@ -1189,12 +1252,13 @@ pub const Dir = struct {
}
while (true) {
- const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,
- &self.darwin_seek);
+ const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek);
const err = posix.getErrno(result);
if (err > 0) {
switch (err) {
- posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+ posix.EBADF,
+ posix.EFAULT,
+ posix.ENOTDIR => unreachable,
posix.EINVAL => {
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
continue;
@@ -1202,14 +1266,13 @@ pub const Dir = struct {
else => return unexpectedErrorPosix(err),
}
}
- if (result == 0)
- return null;
+ if (result == 0) return null;
self.index = 0;
self.end_index = result;
break;
}
}
- const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
+ const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
const next_index = self.index + darwin_entry.d_reclen;
self.index = next_index;
@@ -1254,7 +1317,9 @@ pub const Dir = struct {
const err = posix.getErrno(result);
if (err > 0) {
switch (err) {
- posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+ posix.EBADF,
+ posix.EFAULT,
+ posix.ENOTDIR => unreachable,
posix.EINVAL => {
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
continue;
@@ -1262,14 +1327,13 @@ pub const Dir = struct {
else => return unexpectedErrorPosix(err),
}
}
- if (result == 0)
- return null;
+ if (result == 0) return null;
self.index = 0;
self.end_index = result;
break;
}
}
- const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
+ const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
const next_index = self.index + linux_entry.d_reclen;
self.index = next_index;
@@ -1338,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
if (err > 0) {
return switch (err) {
posix.EACCES => error.AccessDenied,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.EIO => error.FileSystem,
posix.ELOOP => error.SymLinkLoop,
posix.ENAMETOOLONG => error.NameTooLong,
@@ -1356,50 +1421,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
}
}
-pub fn sleep(seconds: usize, nanoseconds: usize) void {
- switch(builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
- posixSleep(u63(seconds), u63(nanoseconds));
- },
- Os.windows => {
- const milliseconds = seconds * 1000 + nanoseconds / 1000000;
- windows.Sleep(windows.DWORD(milliseconds));
- },
- else => @compileError("Unsupported OS"),
- }
-}
-
-const u63 = @IntType(false, 63);
-pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
- var req = posix.timespec {
- .tv_sec = seconds,
- .tv_nsec = nanoseconds,
- };
- var rem: posix.timespec = undefined;
- while (true) {
- const ret_val = posix.nanosleep(&req, &rem);
- const err = posix.getErrno(ret_val);
- if (err == 0) return;
- switch (err) {
- posix.EFAULT => unreachable,
- posix.EINVAL => {
- // Sometimes Darwin returns EINVAL for no reason.
- // We treat it as a spurious wakeup.
- return;
- },
- posix.EINTR => {
- req = rem;
- continue;
- },
- else => return,
- }
- }
-}
-
-test "os.sleep" {
- sleep(0, 1);
-}
-
pub fn posix_setuid(uid: u32) !void {
const err = posix.getErrno(posix.setuid(uid));
if (err == 0) return;
@@ -1475,8 +1496,7 @@ pub const ArgIteratorPosix = struct {
}
pub fn next(self: &ArgIteratorPosix) ?[]const u8 {
- if (self.index == self.count)
- return null;
+ if (self.index == self.count) return null;
const s = raw[self.index];
self.index += 1;
@@ -1484,8 +1504,7 @@ pub const ArgIteratorPosix = struct {
}
pub fn skip(self: &ArgIteratorPosix) bool {
- if (self.index == self.count)
- return false;
+ if (self.index == self.count) return false;
self.index += 1;
return true;
@@ -1503,7 +1522,9 @@ pub const ArgIteratorWindows = struct {
quote_count: usize,
seen_quote_count: usize,
- pub const NextError = error{OutOfMemory};
+ pub const NextError = error {
+ OutOfMemory,
+ };
pub fn init() ArgIteratorWindows {
return initWithCmdLine(windows.GetCommandLineA());
@@ -1526,7 +1547,8 @@ pub const ArgIteratorWindows = struct {
const byte = self.cmd_line[self.index];
switch (byte) {
0 => return null,
- ' ', '\t' => continue,
+ ' ',
+ '\t' => continue,
else => break,
}
}
@@ -1540,7 +1562,8 @@ pub const ArgIteratorWindows = struct {
const byte = self.cmd_line[self.index];
switch (byte) {
0 => return false,
- ' ', '\t' => continue,
+ ' ',
+ '\t' => continue,
else => break,
}
}
@@ -1559,7 +1582,8 @@ pub const ArgIteratorWindows = struct {
'\\' => {
backslash_count += 1;
},
- ' ', '\t' => {
+ ' ',
+ '\t' => {
if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
return true;
}
@@ -1599,7 +1623,8 @@ pub const ArgIteratorWindows = struct {
'\\' => {
backslash_count += 1;
},
- ' ', '\t' => {
+ ' ',
+ '\t' => {
try self.emitBackslashes(&buf, backslash_count);
backslash_count = 0;
if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
@@ -1643,7 +1668,6 @@ pub const ArgIteratorWindows = struct {
}
}
}
-
};
pub const ArgIterator = struct {
@@ -1658,7 +1682,7 @@ pub const ArgIterator = struct {
}
pub const NextError = ArgIteratorWindows.NextError;
-
+
/// You must free the returned memory when done.
pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {
if (builtin.os == Os.windows) {
@@ -1733,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void {
}
test "windows arg parsing" {
- testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});
- testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});
- testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"});
- testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"});
- testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"});
- testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"});
-
- testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"",
- [][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"});
+ testWindowsCmdLine(c"a b\tc d", [][]const u8 {
+ "a",
+ "b",
+ "c",
+ "d",
+ });
+ testWindowsCmdLine(c"\"abc\" d e", [][]const u8 {
+ "abc",
+ "d",
+ "e",
+ });
+ testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 {
+ "a\\\\\\b",
+ "de fg",
+ "h",
+ });
+ testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 {
+ "a\\\"b",
+ "c",
+ "d",
+ });
+ testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 {
+ "a\\\\b c",
+ "d",
+ "e",
+ });
+ testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 {
+ "a",
+ "b",
+ "c",
+ "\"d",
+ "f",
+ });
+
+ testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 {
+ ".\\..\\zig-cache\\build",
+ "bin\\zig.exe",
+ ".\\..",
+ ".\\..\\zig-cache",
+ "--help",
+ });
}
fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void {
@@ -1753,27 +1809,16 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
assert(it.next(debug.global_allocator) == null);
}
-test "std.os" {
- _ = @import("child_process.zig");
- _ = @import("darwin_errno.zig");
- _ = @import("darwin.zig");
- _ = @import("get_user_id.zig");
- _ = @import("linux/errno.zig");
- //_ = @import("linux_i386.zig");
- _ = @import("linux/x86_64.zig");
- _ = @import("linux/index.zig");
- _ = @import("path.zig");
- _ = @import("windows/index.zig");
- _ = @import("test.zig");
-}
-
-
// TODO make this a build variable that you can set
const unexpected_error_tracing = false;
+const UnexpectedError = error {
+ /// The Operating System returned an undocumented error code.
+ Unexpected,
+};
/// Call this when you made a syscall or something that sets errno
/// and you get an unexpected error.
-pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
+pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
if (unexpected_error_tracing) {
debug.warn("unexpected errno: {}\n", errno);
debug.dumpCurrentStackTrace(null);
@@ -1783,7 +1828,7 @@ pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
-pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {
+pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
if (unexpected_error_tracing) {
debug.warn("unexpected GetLastError(): {}\n", err);
debug.dumpCurrentStackTrace(null);
@@ -1799,7 +1844,8 @@ pub fn openSelfExe() !os.File {
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
},
- Os.macosx, Os.ios => {
+ Os.macosx,
+ Os.ios => {
var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
const self_exe_path = try selfExePath(&fixed_allocator.allocator);
@@ -1811,8 +1857,10 @@ pub fn openSelfExe() !os.File {
test "openSelfExe" {
switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
- else => return, // Unsupported OS.
+ Os.linux,
+ Os.macosx,
+ Os.ios => (try openSelfExe()).close(),
+ else => return, // Unsupported OS.
}
}
@@ -1849,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
try out_path.resize(new_len);
}
},
- Os.macosx, Os.ios => {
+ Os.macosx,
+ Os.ios => {
var u32_len: u32 = 0;
const ret1 = c._NSGetExecutablePath(undefined, &u32_len);
assert(ret1 != 0);
@@ -1877,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
const dir = path.dirname(full_exe_path);
return allocator.shrink(u8, full_exe_path, dir.len);
},
- Os.windows, Os.macosx, Os.ios => {
+ Os.windows,
+ Os.macosx,
+ Os.ios => {
const self_exe_path = try selfExePath(allocator);
errdefer allocator.free(self_exe_path);
const dirname = os.path.dirname(self_exe_path);
@@ -1898,3 +1949,712 @@ pub fn isTty(handle: FileHandle) bool {
}
}
}
+
+pub const PosixSocketError = error {
+ /// Permission to create a socket of the specified type and/or
+ /// pro‐tocol is denied.
+ PermissionDenied,
+
+ /// The implementation does not support the specified address family.
+ AddressFamilyNotSupported,
+
+ /// Unknown protocol, or protocol family not available.
+ ProtocolFamilyNotAvailable,
+
+ /// The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// Insufficient memory is available. The socket cannot be created until sufficient
+ /// resources are freed.
+ SystemResources,
+
+ /// The protocol type or the specified protocol is not supported within this domain.
+ ProtocolNotSupported,
+};
+
+pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
+ const rc = posix.socket(domain, socket_type, protocol);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ posix.EACCES => return PosixSocketError.PermissionDenied,
+ posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported,
+ posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
+ posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
+ posix.ENOBUFS,
+ posix.ENOMEM => return PosixSocketError.SystemResources,
+ posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub const PosixBindError = error {
+ /// The address is protected, and the user is not the superuser.
+ /// For UNIX domain sockets: Search permission is denied on a component
+ /// of the path prefix.
+ AccessDenied,
+
+ /// The given address is already in use, or in the case of Internet domain sockets,
+ /// The port number was specified as zero in the socket
+ /// address structure, but, upon attempting to bind to an ephemeral port, it was
+ /// determined that all port numbers in the ephemeral port range are currently in
+ /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
+ AddressInUse,
+
+ /// sockfd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// The socket is already bound to an address, or addrlen is wrong, or addr is not
+ /// a valid address for this socket's domain.
+ InvalidSocketOrAddress,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// A nonexistent interface was requested or the requested address was not local.
+ AddressNotAvailable,
+
+ /// addr points outside the user's accessible address space.
+ PageFault,
+
+ /// Too many symbolic links were encountered in resolving addr.
+ SymLinkLoop,
+
+ /// addr is too long.
+ NameTooLong,
+
+ /// A component in the directory prefix of the socket pathname does not exist.
+ FileNotFound,
+
+ /// Insufficient kernel memory was available.
+ SystemResources,
+
+ /// A component of the path prefix is not a directory.
+ NotDir,
+
+ /// The socket inode would reside on a read-only filesystem.
+ ReadOnlyFileSystem,
+
+ Unexpected,
+};
+
+/// addr is `&const T` where T is one of the sockaddr
+pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void {
+ const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ posix.EACCES => return PosixBindError.AccessDenied,
+ posix.EADDRINUSE => return PosixBindError.AddressInUse,
+ posix.EBADF => return PosixBindError.InvalidFileDescriptor,
+ posix.EINVAL => return PosixBindError.InvalidSocketOrAddress,
+ posix.ENOTSOCK => return PosixBindError.FileDescriptorNotASocket,
+ posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable,
+ posix.EFAULT => return PosixBindError.PageFault,
+ posix.ELOOP => return PosixBindError.SymLinkLoop,
+ posix.ENAMETOOLONG => return PosixBindError.NameTooLong,
+ posix.ENOENT => return PosixBindError.FileNotFound,
+ posix.ENOMEM => return PosixBindError.SystemResources,
+ posix.ENOTDIR => return PosixBindError.NotDir,
+ posix.EROFS => return PosixBindError.ReadOnlyFileSystem,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+const PosixListenError = error {
+ /// Another socket is already listening on the same port.
+ /// For Internet domain sockets, the socket referred to by sockfd had not previously
+ /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
+ /// was determined that all port numbers in the ephemeral port range are currently in
+ /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+ AddressInUse,
+
+ /// The argument sockfd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// The socket is not of a type that supports the listen() operation.
+ OperationNotSupported,
+
+ Unexpected,
+};
+
+pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void {
+ const rc = posix.listen(sockfd, backlog);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ posix.EADDRINUSE => return PosixListenError.AddressInUse,
+ posix.EBADF => return PosixListenError.InvalidFileDescriptor,
+ posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket,
+ posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub const PosixAcceptError = error {
+ /// The socket is marked nonblocking and no connections are present to be accepted.
+ WouldBlock,
+
+ /// sockfd is not an open file descriptor.
+ FileDescriptorClosed,
+
+ ConnectionAborted,
+
+ /// The addr argument is not in a writable part of the user address space.
+ PageFault,
+
+ /// Socket is not listening for connections, or addrlen is invalid (e.g., is negative),
+ /// or invalid value in flags.
+ InvalidSyscall,
+
+ /// The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// Not enough free memory. This often means that the memory allocation is limited
+ /// by the socket buffer limits, not by the system memory.
+ SystemResources,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// The referenced socket is not of type SOCK_STREAM.
+ OperationNotSupported,
+
+ ProtocolFailure,
+
+ /// Firewall rules forbid connection.
+ BlockedByFirewall,
+
+ Unexpected,
+};
+
+pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 {
+ while (true) {
+ var sockaddr_size = u32(@sizeOf(posix.sockaddr));
+ const rc = posix.accept4(fd, addr, &sockaddr_size, flags);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ posix.EINTR => continue,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EAGAIN => return PosixAcceptError.WouldBlock,
+ posix.EBADF => return PosixAcceptError.FileDescriptorClosed,
+ posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted,
+ posix.EFAULT => return PosixAcceptError.PageFault,
+ posix.EINVAL => return PosixAcceptError.InvalidSyscall,
+ posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
+ posix.ENOBUFS,
+ posix.ENOMEM => return PosixAcceptError.SystemResources,
+ posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
+ posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
+ posix.EPROTO => return PosixAcceptError.ProtocolFailure,
+ posix.EPERM => return PosixAcceptError.BlockedByFirewall,
+ }
+ }
+}
+
+pub const LinuxEpollCreateError = error {
+ /// Invalid value specified in flags.
+ InvalidSyscall,
+
+ /// The per-user limit on the number of epoll instances imposed by
+ /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
+ /// details.
+ /// Or, The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// There was insufficient memory to create the kernel object.
+ SystemResources,
+
+ Unexpected,
+};
+
+pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 {
+ const rc = posix.epoll_create1(flags);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ else => return unexpectedErrorPosix(err),
+
+ posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall,
+ posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded,
+ posix.ENOMEM => return LinuxEpollCreateError.SystemResources,
+ }
+}
+
+pub const LinuxEpollCtlError = error {
+ /// epfd or fd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
+ /// with this epoll instance.
+ FileDescriptorAlreadyPresentInSet,
+
+ /// epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested
+ /// operation op is not supported by this interface, or
+ /// An invalid event type was specified along with EPOLLEXCLUSIVE in events, or
+ /// op was EPOLL_CTL_MOD and events included EPOLLEXCLUSIVE, or
+ /// op was EPOLL_CTL_MOD and the EPOLLEXCLUSIVE flag has previously been applied to
+ /// this epfd, fd pair, or
+ /// EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance.
+ InvalidSyscall,
+
+ /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
+ /// circular loop of epoll instances monitoring one another.
+ OperationCausesCircularLoop,
+
+ /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
+ /// instance.
+ FileDescriptorNotRegistered,
+
+ /// There was insufficient memory to handle the requested op control operation.
+ SystemResources,
+
+ /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
+ /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
+ /// See epoll(7) for further details.
+ UserResourceLimitReached,
+
+ /// The target file fd does not support epoll. This error can occur if fd refers to,
+ /// for example, a regular file or a directory.
+ FileDescriptorIncompatibleWithEpoll,
+
+ Unexpected,
+};
+
+pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void {
+ const rc = posix.epoll_ctl(epfd, op, fd, event);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EBADF => return LinuxEpollCtlError.InvalidFileDescriptor,
+ posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet,
+ posix.EINVAL => return LinuxEpollCtlError.InvalidSyscall,
+ posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop,
+ posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered,
+ posix.ENOMEM => return LinuxEpollCtlError.SystemResources,
+ posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached,
+ posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll,
+ }
+}
+
+pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
+ while (true) {
+ const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return rc,
+ posix.EINTR => continue,
+ posix.EBADF => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ else => unreachable,
+ }
+ }
+}
+
+pub const PosixGetSockNameError = error {
+ /// Insufficient resources were available in the system to perform the operation.
+ SystemResources,
+
+ Unexpected,
+};
+
+pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr {
+ var addr: posix.sockaddr = undefined;
+ var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr);
+ const rc = posix.getsockname(sockfd, &addr, &addrlen);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return addr,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EBADF => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ posix.ENOTSOCK => unreachable,
+ posix.ENOBUFS => return PosixGetSockNameError.SystemResources,
+ }
+}
+
+pub const PosixConnectError = error {
+ /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
+ /// file, or search permission is denied for one of the directories in the path prefix.
+ /// or
+ /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
+ /// the connection request failed because of a local firewall rule.
+ PermissionDenied,
+
+ /// Local address is already in use.
+ AddressInUse,
+
+ /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
+ /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
+ /// in the ephemeral port range are currently in use. See the discussion of
+ /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+ AddressNotAvailable,
+
+ /// The passed address didn't have the correct address family in its sa_family field.
+ AddressFamilyNotSupported,
+
+ /// Insufficient entries in the routing cache.
+ SystemResources,
+
+ /// A connect() on a stream socket found no one listening on the remote address.
+ ConnectionRefused,
+
+ /// Network is unreachable.
+ NetworkUnreachable,
+
+ /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
+ /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
+ ConnectionTimedOut,
+
+ Unexpected,
+};
+
+pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
+ while (true) {
+ const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
+ posix.EINTR => continue,
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ }
+ }
+}
+
+/// Same as posixConnect except it is for blocking socket file descriptors.
+/// It expects to receive EINPROGRESS.
+pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
+ while (true) {
+ const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0,
+ posix.EINPROGRESS => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EINTR => continue,
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ }
+ }
+}
+
+pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
+ var err_code: i32 = undefined;
+ var size: u32 = @sizeOf(i32);
+ const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size);
+ assert(size == 4);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => switch (err_code) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ },
+ else => return unexpectedErrorPosix(err),
+ posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
+ posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
+ posix.EINVAL => unreachable,
+ posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ }
+}
+
+pub const Thread = struct {
+ data: Data,
+
+ pub const use_pthreads = is_posix and builtin.link_libc;
+ const Data = if (use_pthreads) struct {
+ handle: c.pthread_t,
+ stack_addr: usize,
+ stack_len: usize,
+ } else switch (builtin.os) {
+ builtin.Os.linux => struct {
+ pid: i32,
+ stack_addr: usize,
+ stack_len: usize,
+ },
+ builtin.Os.windows => struct {
+ handle: windows.HANDLE,
+ alloc_start: &c_void,
+ heap_handle: windows.HANDLE,
+ },
+ else => @compileError("Unsupported OS"),
+ };
+
+ pub fn wait(self: &const Thread) void {
+ if (use_pthreads) {
+ const err = c.pthread_join(self.data.handle, null);
+ switch (err) {
+ 0 => {},
+ posix.EINVAL => unreachable,
+ posix.ESRCH => unreachable,
+ posix.EDEADLK => unreachable,
+ else => unreachable,
+ }
+ assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ } else switch (builtin.os) {
+ builtin.Os.linux => {
+ while (true) {
+ const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst);
+ if (pid_value == 0) break;
+ const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null);
+ switch (linux.getErrno(rc)) {
+ 0 => continue,
+ posix.EINTR => continue,
+ posix.EAGAIN => continue,
+ else => unreachable,
+ }
+ }
+ assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ },
+ builtin.Os.windows => {
+ assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
+ assert(windows.CloseHandle(self.data.handle) != 0);
+ assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
+ },
+ else => @compileError("Unsupported OS"),
+ }
+ }
+};
+
+pub const SpawnThreadError = error {
+ /// A system-imposed limit on the number of threads was encountered.
+ /// There are a number of limits that may trigger this error:
+ /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
+ /// which limits the number of processes and threads for a real
+ /// user ID, was reached;
+ /// * the kernel's system-wide limit on the number of processes and
+ /// threads, /proc/sys/kernel/threads-max, was reached (see
+ /// proc(5));
+ /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was
+ /// reached (see proc(5)); or
+ /// * the PID limit (pids.max) imposed by the cgroup "process num‐
+ /// ber" (PIDs) controller was reached.
+ ThreadQuotaExceeded,
+
+ /// The kernel cannot allocate sufficient memory to allocate a task structure
+ /// for the child, or to copy those parts of the caller's context that need to
+ /// be copied.
+ SystemResources,
+
+ /// Not enough userland memory to spawn the thread.
+ OutOfMemory,
+
+ Unexpected,
+};
+
+/// caller must call wait on the returned thread
+/// fn startFn(@typeOf(context)) T
+/// where T is u8, noreturn, void, or !void
+/// caller must call wait on the returned thread
+pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread {
+ // TODO compile-time call graph analysis to determine stack upper bound
+ // https://github.com/zig-lang/zig/issues/157
+ const default_stack_size = 8 * 1024 * 1024;
+
+ const Context = @typeOf(context);
+ comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
+
+ if (builtin.os == builtin.Os.windows) {
+ const WinThread = struct {
+ const OuterContext = struct {
+ thread: Thread,
+ inner: Context,
+ };
+ extern fn threadMain(arg: windows.LPVOID) windows.DWORD {
+ if (@sizeOf(Context) == 0) {
+ return startFn({});
+ } else {
+ return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg)));
+ }
+ }
+ };
+
+ const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory;
+ const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
+ const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory;
+ errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
+ const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count];
+ const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
+ outer_context.inner = context;
+ outer_context.thread.data.heap_handle = heap_handle;
+ outer_context.thread.data.alloc_start = bytes_ptr;
+
+ const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner);
+ outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? {
+ const err = windows.GetLastError();
+ return switch (err) {
+ else => os.unexpectedErrorWindows(err),
+ };
+ };
+ return &outer_context.thread;
+ }
+
+ const MainFuncs = struct {
+ extern fn linuxThreadMain(ctx_addr: usize) u8 {
+ if (@sizeOf(Context) == 0) {
+ return startFn({});
+ } else {
+ return startFn(*@intToPtr(&const Context, ctx_addr));
+ }
+ }
+ extern fn posixThreadMain(ctx: ?&c_void) ?&c_void {
+ if (@sizeOf(Context) == 0) {
+ _ = startFn({});
+ return null;
+ } else {
+ _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)));
+ return null;
+ }
+ }
+ };
+
+ const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
+
+ const mmap_len = default_stack_size;
+ const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
+ if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
+ errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
+
+ var stack_end: usize = stack_addr + mmap_len;
+ var arg: usize = undefined;
+ if (@sizeOf(Context) != 0) {
+ stack_end -= @sizeOf(Context);
+ stack_end -= stack_end % @alignOf(Context);
+ assert(stack_end >= stack_addr);
+ const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
+ *context_ptr = context;
+ arg = stack_end;
+ }
+
+ stack_end -= @sizeOf(Thread);
+ stack_end -= stack_end % @alignOf(Thread);
+ assert(stack_end >= stack_addr);
+ const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
+
+ thread_ptr.data.stack_addr = stack_addr;
+ thread_ptr.data.stack_len = mmap_len;
+
+ if (builtin.os == builtin.Os.windows) {
+ // use windows API directly
+ @compileError("TODO support spawnThread for Windows");
+ } else if (Thread.use_pthreads) {
+ // use pthreads
+ var attr: c.pthread_attr_t = undefined;
+ if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
+ defer assert(c.pthread_attr_destroy(&attr) == 0);
+
+ // align to page
+ stack_end -= stack_end % os.page_size;
+ assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0);
+
+ const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
+ switch (err) {
+ 0 => return thread_ptr,
+ posix.EAGAIN => return SpawnThreadError.SystemResources,
+ posix.EPERM => unreachable,
+ posix.EINVAL => unreachable,
+ else => return unexpectedErrorPosix(usize(err)),
+ }
+ } else if (builtin.os == builtin.Os.linux) {
+ // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
+ const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
+ const newtls: usize = 0;
+ const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return thread_ptr,
+ posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
+ posix.EINVAL => unreachable,
+ posix.ENOMEM => return SpawnThreadError.SystemResources,
+ posix.ENOSPC => unreachable,
+ posix.EPERM => unreachable,
+ posix.EUSERS => unreachable,
+ else => return unexpectedErrorPosix(err),
+ }
+ } else {
+ @compileError("Unsupported OS");
+ }
+}
+
+pub fn posixWait(pid: i32) i32 {
+ var status: i32 = undefined;
+ while (true) {
+ const err = posix.getErrno(posix.waitpid(pid, &status, 0));
+ switch (err) {
+ 0 => return status,
+ posix.EINTR => continue,
+ posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
+ posix.EINVAL => unreachable, // The options argument was invalid
+ else => unreachable,
+ }
+ }
+}
diff --git a/std/os/linux/i386.zig b/std/os/linux/i386.zig
deleted file mode 100644
index 7450ad34fa..0000000000
--- a/std/os/linux/i386.zig
+++ /dev/null
@@ -1,505 +0,0 @@
-const std = @import("../../index.zig");
-const linux = std.os.linux;
-const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-
-pub const SYS_restart_syscall = 0;
-pub const SYS_exit = 1;
-pub const SYS_fork = 2;
-pub const SYS_read = 3;
-pub const SYS_write = 4;
-pub const SYS_open = 5;
-pub const SYS_close = 6;
-pub const SYS_waitpid = 7;
-pub const SYS_creat = 8;
-pub const SYS_link = 9;
-pub const SYS_unlink = 10;
-pub const SYS_execve = 11;
-pub const SYS_chdir = 12;
-pub const SYS_time = 13;
-pub const SYS_mknod = 14;
-pub const SYS_chmod = 15;
-pub const SYS_lchown = 16;
-pub const SYS_break = 17;
-pub const SYS_oldstat = 18;
-pub const SYS_lseek = 19;
-pub const SYS_getpid = 20;
-pub const SYS_mount = 21;
-pub const SYS_umount = 22;
-pub const SYS_setuid = 23;
-pub const SYS_getuid = 24;
-pub const SYS_stime = 25;
-pub const SYS_ptrace = 26;
-pub const SYS_alarm = 27;
-pub const SYS_oldfstat = 28;
-pub const SYS_pause = 29;
-pub const SYS_utime = 30;
-pub const SYS_stty = 31;
-pub const SYS_gtty = 32;
-pub const SYS_access = 33;
-pub const SYS_nice = 34;
-pub const SYS_ftime = 35;
-pub const SYS_sync = 36;
-pub const SYS_kill = 37;
-pub const SYS_rename = 38;
-pub const SYS_mkdir = 39;
-pub const SYS_rmdir = 40;
-pub const SYS_dup = 41;
-pub const SYS_pipe = 42;
-pub const SYS_times = 43;
-pub const SYS_prof = 44;
-pub const SYS_brk = 45;
-pub const SYS_setgid = 46;
-pub const SYS_getgid = 47;
-pub const SYS_signal = 48;
-pub const SYS_geteuid = 49;
-pub const SYS_getegid = 50;
-pub const SYS_acct = 51;
-pub const SYS_umount2 = 52;
-pub const SYS_lock = 53;
-pub const SYS_ioctl = 54;
-pub const SYS_fcntl = 55;
-pub const SYS_mpx = 56;
-pub const SYS_setpgid = 57;
-pub const SYS_ulimit = 58;
-pub const SYS_oldolduname = 59;
-pub const SYS_umask = 60;
-pub const SYS_chroot = 61;
-pub const SYS_ustat = 62;
-pub const SYS_dup2 = 63;
-pub const SYS_getppid = 64;
-pub const SYS_getpgrp = 65;
-pub const SYS_setsid = 66;
-pub const SYS_sigaction = 67;
-pub const SYS_sgetmask = 68;
-pub const SYS_ssetmask = 69;
-pub const SYS_setreuid = 70;
-pub const SYS_setregid = 71;
-pub const SYS_sigsuspend = 72;
-pub const SYS_sigpending = 73;
-pub const SYS_sethostname = 74;
-pub const SYS_setrlimit = 75;
-pub const SYS_getrlimit = 76;
-pub const SYS_getrusage = 77;
-pub const SYS_gettimeofday = 78;
-pub const SYS_settimeofday = 79;
-pub const SYS_getgroups = 80;
-pub const SYS_setgroups = 81;
-pub const SYS_select = 82;
-pub const SYS_symlink = 83;
-pub const SYS_oldlstat = 84;
-pub const SYS_readlink = 85;
-pub const SYS_uselib = 86;
-pub const SYS_swapon = 87;
-pub const SYS_reboot = 88;
-pub const SYS_readdir = 89;
-pub const SYS_mmap = 90;
-pub const SYS_munmap = 91;
-pub const SYS_truncate = 92;
-pub const SYS_ftruncate = 93;
-pub const SYS_fchmod = 94;
-pub const SYS_fchown = 95;
-pub const SYS_getpriority = 96;
-pub const SYS_setpriority = 97;
-pub const SYS_profil = 98;
-pub const SYS_statfs = 99;
-pub const SYS_fstatfs = 100;
-pub const SYS_ioperm = 101;
-pub const SYS_socketcall = 102;
-pub const SYS_syslog = 103;
-pub const SYS_setitimer = 104;
-pub const SYS_getitimer = 105;
-pub const SYS_stat = 106;
-pub const SYS_lstat = 107;
-pub const SYS_fstat = 108;
-pub const SYS_olduname = 109;
-pub const SYS_iopl = 110;
-pub const SYS_vhangup = 111;
-pub const SYS_idle = 112;
-pub const SYS_vm86old = 113;
-pub const SYS_wait4 = 114;
-pub const SYS_swapoff = 115;
-pub const SYS_sysinfo = 116;
-pub const SYS_ipc = 117;
-pub const SYS_fsync = 118;
-pub const SYS_sigreturn = 119;
-pub const SYS_clone = 120;
-pub const SYS_setdomainname = 121;
-pub const SYS_uname = 122;
-pub const SYS_modify_ldt = 123;
-pub const SYS_adjtimex = 124;
-pub const SYS_mprotect = 125;
-pub const SYS_sigprocmask = 126;
-pub const SYS_create_module = 127;
-pub const SYS_init_module = 128;
-pub const SYS_delete_module = 129;
-pub const SYS_get_kernel_syms = 130;
-pub const SYS_quotactl = 131;
-pub const SYS_getpgid = 132;
-pub const SYS_fchdir = 133;
-pub const SYS_bdflush = 134;
-pub const SYS_sysfs = 135;
-pub const SYS_personality = 136;
-pub const SYS_afs_syscall = 137;
-pub const SYS_setfsuid = 138;
-pub const SYS_setfsgid = 139;
-pub const SYS__llseek = 140;
-pub const SYS_getdents = 141;
-pub const SYS__newselect = 142;
-pub const SYS_flock = 143;
-pub const SYS_msync = 144;
-pub const SYS_readv = 145;
-pub const SYS_writev = 146;
-pub const SYS_getsid = 147;
-pub const SYS_fdatasync = 148;
-pub const SYS__sysctl = 149;
-pub const SYS_mlock = 150;
-pub const SYS_munlock = 151;
-pub const SYS_mlockall = 152;
-pub const SYS_munlockall = 153;
-pub const SYS_sched_setparam = 154;
-pub const SYS_sched_getparam = 155;
-pub const SYS_sched_setscheduler = 156;
-pub const SYS_sched_getscheduler = 157;
-pub const SYS_sched_yield = 158;
-pub const SYS_sched_get_priority_max = 159;
-pub const SYS_sched_get_priority_min = 160;
-pub const SYS_sched_rr_get_interval = 161;
-pub const SYS_nanosleep = 162;
-pub const SYS_mremap = 163;
-pub const SYS_setresuid = 164;
-pub const SYS_getresuid = 165;
-pub const SYS_vm86 = 166;
-pub const SYS_query_module = 167;
-pub const SYS_poll = 168;
-pub const SYS_nfsservctl = 169;
-pub const SYS_setresgid = 170;
-pub const SYS_getresgid = 171;
-pub const SYS_prctl = 172;
-pub const SYS_rt_sigreturn = 173;
-pub const SYS_rt_sigaction = 174;
-pub const SYS_rt_sigprocmask = 175;
-pub const SYS_rt_sigpending = 176;
-pub const SYS_rt_sigtimedwait = 177;
-pub const SYS_rt_sigqueueinfo = 178;
-pub const SYS_rt_sigsuspend = 179;
-pub const SYS_pread64 = 180;
-pub const SYS_pwrite64 = 181;
-pub const SYS_chown = 182;
-pub const SYS_getcwd = 183;
-pub const SYS_capget = 184;
-pub const SYS_capset = 185;
-pub const SYS_sigaltstack = 186;
-pub const SYS_sendfile = 187;
-pub const SYS_getpmsg = 188;
-pub const SYS_putpmsg = 189;
-pub const SYS_vfork = 190;
-pub const SYS_ugetrlimit = 191;
-pub const SYS_mmap2 = 192;
-pub const SYS_truncate64 = 193;
-pub const SYS_ftruncate64 = 194;
-pub const SYS_stat64 = 195;
-pub const SYS_lstat64 = 196;
-pub const SYS_fstat64 = 197;
-pub const SYS_lchown32 = 198;
-pub const SYS_getuid32 = 199;
-pub const SYS_getgid32 = 200;
-pub const SYS_geteuid32 = 201;
-pub const SYS_getegid32 = 202;
-pub const SYS_setreuid32 = 203;
-pub const SYS_setregid32 = 204;
-pub const SYS_getgroups32 = 205;
-pub const SYS_setgroups32 = 206;
-pub const SYS_fchown32 = 207;
-pub const SYS_setresuid32 = 208;
-pub const SYS_getresuid32 = 209;
-pub const SYS_setresgid32 = 210;
-pub const SYS_getresgid32 = 211;
-pub const SYS_chown32 = 212;
-pub const SYS_setuid32 = 213;
-pub const SYS_setgid32 = 214;
-pub const SYS_setfsuid32 = 215;
-pub const SYS_setfsgid32 = 216;
-pub const SYS_pivot_root = 217;
-pub const SYS_mincore = 218;
-pub const SYS_madvise = 219;
-pub const SYS_madvise1 = 219;
-pub const SYS_getdents64 = 220;
-pub const SYS_fcntl64 = 221;
-pub const SYS_gettid = 224;
-pub const SYS_readahead = 225;
-pub const SYS_setxattr = 226;
-pub const SYS_lsetxattr = 227;
-pub const SYS_fsetxattr = 228;
-pub const SYS_getxattr = 229;
-pub const SYS_lgetxattr = 230;
-pub const SYS_fgetxattr = 231;
-pub const SYS_listxattr = 232;
-pub const SYS_llistxattr = 233;
-pub const SYS_flistxattr = 234;
-pub const SYS_removexattr = 235;
-pub const SYS_lremovexattr = 236;
-pub const SYS_fremovexattr = 237;
-pub const SYS_tkill = 238;
-pub const SYS_sendfile64 = 239;
-pub const SYS_futex = 240;
-pub const SYS_sched_setaffinity = 241;
-pub const SYS_sched_getaffinity = 242;
-pub const SYS_set_thread_area = 243;
-pub const SYS_get_thread_area = 244;
-pub const SYS_io_setup = 245;
-pub const SYS_io_destroy = 246;
-pub const SYS_io_getevents = 247;
-pub const SYS_io_submit = 248;
-pub const SYS_io_cancel = 249;
-pub const SYS_fadvise64 = 250;
-pub const SYS_exit_group = 252;
-pub const SYS_lookup_dcookie = 253;
-pub const SYS_epoll_create = 254;
-pub const SYS_epoll_ctl = 255;
-pub const SYS_epoll_wait = 256;
-pub const SYS_remap_file_pages = 257;
-pub const SYS_set_tid_address = 258;
-pub const SYS_timer_create = 259;
-pub const SYS_timer_settime = SYS_timer_create+1;
-pub const SYS_timer_gettime = SYS_timer_create+2;
-pub const SYS_timer_getoverrun = SYS_timer_create+3;
-pub const SYS_timer_delete = SYS_timer_create+4;
-pub const SYS_clock_settime = SYS_timer_create+5;
-pub const SYS_clock_gettime = SYS_timer_create+6;
-pub const SYS_clock_getres = SYS_timer_create+7;
-pub const SYS_clock_nanosleep = SYS_timer_create+8;
-pub const SYS_statfs64 = 268;
-pub const SYS_fstatfs64 = 269;
-pub const SYS_tgkill = 270;
-pub const SYS_utimes = 271;
-pub const SYS_fadvise64_64 = 272;
-pub const SYS_vserver = 273;
-pub const SYS_mbind = 274;
-pub const SYS_get_mempolicy = 275;
-pub const SYS_set_mempolicy = 276;
-pub const SYS_mq_open = 277;
-pub const SYS_mq_unlink = SYS_mq_open+1;
-pub const SYS_mq_timedsend = SYS_mq_open+2;
-pub const SYS_mq_timedreceive = SYS_mq_open+3;
-pub const SYS_mq_notify = SYS_mq_open+4;
-pub const SYS_mq_getsetattr = SYS_mq_open+5;
-pub const SYS_kexec_load = 283;
-pub const SYS_waitid = 284;
-pub const SYS_add_key = 286;
-pub const SYS_request_key = 287;
-pub const SYS_keyctl = 288;
-pub const SYS_ioprio_set = 289;
-pub const SYS_ioprio_get = 290;
-pub const SYS_inotify_init = 291;
-pub const SYS_inotify_add_watch = 292;
-pub const SYS_inotify_rm_watch = 293;
-pub const SYS_migrate_pages = 294;
-pub const SYS_openat = 295;
-pub const SYS_mkdirat = 296;
-pub const SYS_mknodat = 297;
-pub const SYS_fchownat = 298;
-pub const SYS_futimesat = 299;
-pub const SYS_fstatat64 = 300;
-pub const SYS_unlinkat = 301;
-pub const SYS_renameat = 302;
-pub const SYS_linkat = 303;
-pub const SYS_symlinkat = 304;
-pub const SYS_readlinkat = 305;
-pub const SYS_fchmodat = 306;
-pub const SYS_faccessat = 307;
-pub const SYS_pselect6 = 308;
-pub const SYS_ppoll = 309;
-pub const SYS_unshare = 310;
-pub const SYS_set_robust_list = 311;
-pub const SYS_get_robust_list = 312;
-pub const SYS_splice = 313;
-pub const SYS_sync_file_range = 314;
-pub const SYS_tee = 315;
-pub const SYS_vmsplice = 316;
-pub const SYS_move_pages = 317;
-pub const SYS_getcpu = 318;
-pub const SYS_epoll_pwait = 319;
-pub const SYS_utimensat = 320;
-pub const SYS_signalfd = 321;
-pub const SYS_timerfd_create = 322;
-pub const SYS_eventfd = 323;
-pub const SYS_fallocate = 324;
-pub const SYS_timerfd_settime = 325;
-pub const SYS_timerfd_gettime = 326;
-pub const SYS_signalfd4 = 327;
-pub const SYS_eventfd2 = 328;
-pub const SYS_epoll_create1 = 329;
-pub const SYS_dup3 = 330;
-pub const SYS_pipe2 = 331;
-pub const SYS_inotify_init1 = 332;
-pub const SYS_preadv = 333;
-pub const SYS_pwritev = 334;
-pub const SYS_rt_tgsigqueueinfo = 335;
-pub const SYS_perf_event_open = 336;
-pub const SYS_recvmmsg = 337;
-pub const SYS_fanotify_init = 338;
-pub const SYS_fanotify_mark = 339;
-pub const SYS_prlimit64 = 340;
-pub const SYS_name_to_handle_at = 341;
-pub const SYS_open_by_handle_at = 342;
-pub const SYS_clock_adjtime = 343;
-pub const SYS_syncfs = 344;
-pub const SYS_sendmmsg = 345;
-pub const SYS_setns = 346;
-pub const SYS_process_vm_readv = 347;
-pub const SYS_process_vm_writev = 348;
-pub const SYS_kcmp = 349;
-pub const SYS_finit_module = 350;
-pub const SYS_sched_setattr = 351;
-pub const SYS_sched_getattr = 352;
-pub const SYS_renameat2 = 353;
-pub const SYS_seccomp = 354;
-pub const SYS_getrandom = 355;
-pub const SYS_memfd_create = 356;
-pub const SYS_bpf = 357;
-pub const SYS_execveat = 358;
-pub const SYS_socket = 359;
-pub const SYS_socketpair = 360;
-pub const SYS_bind = 361;
-pub const SYS_connect = 362;
-pub const SYS_listen = 363;
-pub const SYS_accept4 = 364;
-pub const SYS_getsockopt = 365;
-pub const SYS_setsockopt = 366;
-pub const SYS_getsockname = 367;
-pub const SYS_getpeername = 368;
-pub const SYS_sendto = 369;
-pub const SYS_sendmsg = 370;
-pub const SYS_recvfrom = 371;
-pub const SYS_recvmsg = 372;
-pub const SYS_shutdown = 373;
-pub const SYS_userfaultfd = 374;
-pub const SYS_membarrier = 375;
-pub const SYS_mlock2 = 376;
-
-
-pub const O_CREAT = 0o100;
-pub const O_EXCL = 0o200;
-pub const O_NOCTTY = 0o400;
-pub const O_TRUNC = 0o1000;
-pub const O_APPEND = 0o2000;
-pub const O_NONBLOCK = 0o4000;
-pub const O_DSYNC = 0o10000;
-pub const O_SYNC = 0o4010000;
-pub const O_RSYNC = 0o4010000;
-pub const O_DIRECTORY = 0o200000;
-pub const O_NOFOLLOW = 0o400000;
-pub const O_CLOEXEC = 0o2000000;
-
-pub const O_ASYNC = 0o20000;
-pub const O_DIRECT = 0o40000;
-pub const O_LARGEFILE = 0o100000;
-pub const O_NOATIME = 0o1000000;
-pub const O_PATH = 0o10000000;
-pub const O_TMPFILE = 0o20200000;
-pub const O_NDELAY = O_NONBLOCK;
-
-pub const F_DUPFD = 0;
-pub const F_GETFD = 1;
-pub const F_SETFD = 2;
-pub const F_GETFL = 3;
-pub const F_SETFL = 4;
-
-pub const F_SETOWN = 8;
-pub const F_GETOWN = 9;
-pub const F_SETSIG = 10;
-pub const F_GETSIG = 11;
-
-pub const F_GETLK = 12;
-pub const F_SETLK = 13;
-pub const F_SETLKW = 14;
-
-pub const F_SETOWN_EX = 15;
-pub const F_GETOWN_EX = 16;
-
-pub const F_GETOWNER_UIDS = 17;
-
-pub inline fn syscall0(number: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number));
-}
-
-pub inline fn syscall1(number: usize, arg1: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1));
-}
-
-pub inline fn syscall2(number: usize, arg1: usize, arg2: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2));
-}
-
-pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3));
-}
-
-pub inline fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4));
-}
-
-pub inline fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize,
- arg4: usize, arg5: usize) usize
-{
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4),
- [arg5] "{edi}" (arg5));
-}
-
-pub inline fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize,
- arg4: usize, arg5: usize, arg6: usize) usize
-{
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4),
- [arg5] "{edi}" (arg5),
- [arg6] "{ebp}" (arg6));
-}
-
-pub nakedcc fn restore() void {
- asm volatile (
- \\popl %%eax
- \\movl $119, %%eax
- \\int $0x80
- :
- :
- : "rcx", "r11");
-}
-
-pub nakedcc fn restore_rt() void {
- asm volatile ("int $0x80"
- :
- : [number] "{eax}" (usize(SYS_rt_sigreturn))
- : "rcx", "r11");
-}
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 8fd8bcbe78..368f074b9b 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -1,6 +1,7 @@
const std = @import("../../index.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
+const vdso = @import("vdso.zig");
pub use switch (builtin.arch) {
builtin.Arch.x86_64 => @import("x86_64.zig"),
builtin.Arch.i386 => @import("i386.zig"),
@@ -14,6 +15,22 @@ pub const STDIN_FILENO = 0;
pub const STDOUT_FILENO = 1;
pub const STDERR_FILENO = 2;
+pub const FUTEX_WAIT = 0;
+pub const FUTEX_WAKE = 1;
+pub const FUTEX_FD = 2;
+pub const FUTEX_REQUEUE = 3;
+pub const FUTEX_CMP_REQUEUE = 4;
+pub const FUTEX_WAKE_OP = 5;
+pub const FUTEX_LOCK_PI = 6;
+pub const FUTEX_UNLOCK_PI = 7;
+pub const FUTEX_TRYLOCK_PI = 8;
+pub const FUTEX_WAIT_BITSET = 9;
+
+pub const FUTEX_PRIVATE_FLAG = 128;
+
+pub const FUTEX_CLOCK_REALTIME = 256;
+
+
pub const PROT_NONE = 0;
pub const PROT_READ = 1;
pub const PROT_WRITE = 2;
@@ -38,6 +55,11 @@ pub const MAP_STACK = 0x20000;
pub const MAP_HUGETLB = 0x40000;
pub const MAP_FILE = 0;
+pub const F_OK = 0;
+pub const X_OK = 1;
+pub const W_OK = 2;
+pub const R_OK = 4;
+
pub const WNOHANG = 1;
pub const WUNTRACED = 2;
pub const WSTOPPED = 2;
@@ -101,17 +123,6 @@ pub const SIG_BLOCK = 0;
pub const SIG_UNBLOCK = 1;
pub const SIG_SETMASK = 2;
-pub const SOCK_STREAM = 1;
-pub const SOCK_DGRAM = 2;
-pub const SOCK_RAW = 3;
-pub const SOCK_RDM = 4;
-pub const SOCK_SEQPACKET = 5;
-pub const SOCK_DCCP = 6;
-pub const SOCK_PACKET = 10;
-pub const SOCK_CLOEXEC = 0o2000000;
-pub const SOCK_NONBLOCK = 0o4000;
-
-
pub const PROTO_ip = 0o000;
pub const PROTO_icmp = 0o001;
pub const PROTO_igmp = 0o002;
@@ -149,6 +160,20 @@ pub const PROTO_encap = 0o142;
pub const PROTO_pim = 0o147;
pub const PROTO_raw = 0o377;
+pub const SHUT_RD = 0;
+pub const SHUT_WR = 1;
+pub const SHUT_RDWR = 2;
+
+pub const SOCK_STREAM = 1;
+pub const SOCK_DGRAM = 2;
+pub const SOCK_RAW = 3;
+pub const SOCK_RDM = 4;
+pub const SOCK_SEQPACKET = 5;
+pub const SOCK_DCCP = 6;
+pub const SOCK_PACKET = 10;
+pub const SOCK_CLOEXEC = 0o2000000;
+pub const SOCK_NONBLOCK = 0o4000;
+
pub const PF_UNSPEC = 0;
pub const PF_LOCAL = 1;
pub const PF_UNIX = PF_LOCAL;
@@ -193,7 +218,10 @@ pub const PF_CAIF = 37;
pub const PF_ALG = 38;
pub const PF_NFC = 39;
pub const PF_VSOCK = 40;
-pub const PF_MAX = 41;
+pub const PF_KCM = 41;
+pub const PF_QIPCRTR = 42;
+pub const PF_SMC = 43;
+pub const PF_MAX = 44;
pub const AF_UNSPEC = PF_UNSPEC;
pub const AF_LOCAL = PF_LOCAL;
@@ -239,8 +267,137 @@ pub const AF_CAIF = PF_CAIF;
pub const AF_ALG = PF_ALG;
pub const AF_NFC = PF_NFC;
pub const AF_VSOCK = PF_VSOCK;
+pub const AF_KCM = PF_KCM;
+pub const AF_QIPCRTR = PF_QIPCRTR;
+pub const AF_SMC = PF_SMC;
pub const AF_MAX = PF_MAX;
+pub const SO_DEBUG = 1;
+pub const SO_REUSEADDR = 2;
+pub const SO_TYPE = 3;
+pub const SO_ERROR = 4;
+pub const SO_DONTROUTE = 5;
+pub const SO_BROADCAST = 6;
+pub const SO_SNDBUF = 7;
+pub const SO_RCVBUF = 8;
+pub const SO_KEEPALIVE = 9;
+pub const SO_OOBINLINE = 10;
+pub const SO_NO_CHECK = 11;
+pub const SO_PRIORITY = 12;
+pub const SO_LINGER = 13;
+pub const SO_BSDCOMPAT = 14;
+pub const SO_REUSEPORT = 15;
+pub const SO_PASSCRED = 16;
+pub const SO_PEERCRED = 17;
+pub const SO_RCVLOWAT = 18;
+pub const SO_SNDLOWAT = 19;
+pub const SO_RCVTIMEO = 20;
+pub const SO_SNDTIMEO = 21;
+pub const SO_ACCEPTCONN = 30;
+pub const SO_SNDBUFFORCE = 32;
+pub const SO_RCVBUFFORCE = 33;
+pub const SO_PROTOCOL = 38;
+pub const SO_DOMAIN = 39;
+
+pub const SO_SECURITY_AUTHENTICATION = 22;
+pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23;
+pub const SO_SECURITY_ENCRYPTION_NETWORK = 24;
+
+pub const SO_BINDTODEVICE = 25;
+
+pub const SO_ATTACH_FILTER = 26;
+pub const SO_DETACH_FILTER = 27;
+pub const SO_GET_FILTER = SO_ATTACH_FILTER;
+
+pub const SO_PEERNAME = 28;
+pub const SO_TIMESTAMP = 29;
+pub const SCM_TIMESTAMP = SO_TIMESTAMP;
+
+pub const SO_PEERSEC = 31;
+pub const SO_PASSSEC = 34;
+pub const SO_TIMESTAMPNS = 35;
+pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS;
+pub const SO_MARK = 36;
+pub const SO_TIMESTAMPING = 37;
+pub const SCM_TIMESTAMPING = SO_TIMESTAMPING;
+pub const SO_RXQ_OVFL = 40;
+pub const SO_WIFI_STATUS = 41;
+pub const SCM_WIFI_STATUS = SO_WIFI_STATUS;
+pub const SO_PEEK_OFF = 42;
+pub const SO_NOFCS = 43;
+pub const SO_LOCK_FILTER = 44;
+pub const SO_SELECT_ERR_QUEUE = 45;
+pub const SO_BUSY_POLL = 46;
+pub const SO_MAX_PACING_RATE = 47;
+pub const SO_BPF_EXTENSIONS = 48;
+pub const SO_INCOMING_CPU = 49;
+pub const SO_ATTACH_BPF = 50;
+pub const SO_DETACH_BPF = SO_DETACH_FILTER;
+pub const SO_ATTACH_REUSEPORT_CBPF = 51;
+pub const SO_ATTACH_REUSEPORT_EBPF = 52;
+pub const SO_CNX_ADVICE = 53;
+pub const SCM_TIMESTAMPING_OPT_STATS = 54;
+pub const SO_MEMINFO = 55;
+pub const SO_INCOMING_NAPI_ID = 56;
+pub const SO_COOKIE = 57;
+pub const SCM_TIMESTAMPING_PKTINFO = 58;
+pub const SO_PEERGROUPS = 59;
+pub const SO_ZEROCOPY = 60;
+
+pub const SOL_SOCKET = 1;
+
+pub const SOL_IP = 0;
+pub const SOL_IPV6 = 41;
+pub const SOL_ICMPV6 = 58;
+
+pub const SOL_RAW = 255;
+pub const SOL_DECNET = 261;
+pub const SOL_X25 = 262;
+pub const SOL_PACKET = 263;
+pub const SOL_ATM = 264;
+pub const SOL_AAL = 265;
+pub const SOL_IRDA = 266;
+pub const SOL_NETBEUI = 267;
+pub const SOL_LLC = 268;
+pub const SOL_DCCP = 269;
+pub const SOL_NETLINK = 270;
+pub const SOL_TIPC = 271;
+pub const SOL_RXRPC = 272;
+pub const SOL_PPPOL2TP = 273;
+pub const SOL_BLUETOOTH = 274;
+pub const SOL_PNPIPE = 275;
+pub const SOL_RDS = 276;
+pub const SOL_IUCV = 277;
+pub const SOL_CAIF = 278;
+pub const SOL_ALG = 279;
+pub const SOL_NFC = 280;
+pub const SOL_KCM = 281;
+pub const SOL_TLS = 282;
+
+pub const SOMAXCONN = 128;
+
+pub const MSG_OOB = 0x0001;
+pub const MSG_PEEK = 0x0002;
+pub const MSG_DONTROUTE = 0x0004;
+pub const MSG_CTRUNC = 0x0008;
+pub const MSG_PROXY = 0x0010;
+pub const MSG_TRUNC = 0x0020;
+pub const MSG_DONTWAIT = 0x0040;
+pub const MSG_EOR = 0x0080;
+pub const MSG_WAITALL = 0x0100;
+pub const MSG_FIN = 0x0200;
+pub const MSG_SYN = 0x0400;
+pub const MSG_CONFIRM = 0x0800;
+pub const MSG_RST = 0x1000;
+pub const MSG_ERRQUEUE = 0x2000;
+pub const MSG_NOSIGNAL = 0x4000;
+pub const MSG_MORE = 0x8000;
+pub const MSG_WAITFORONE = 0x10000;
+pub const MSG_BATCH = 0x40000;
+pub const MSG_ZEROCOPY = 0x4000000;
+pub const MSG_FASTOPEN = 0x20000000;
+pub const MSG_CMSG_CLOEXEC = 0x40000000;
+
pub const DT_UNKNOWN = 0;
pub const DT_FIFO = 1;
pub const DT_CHR = 2;
@@ -343,6 +500,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9;
pub const CLOCK_SGI_CYCLE = 10;
pub const CLOCK_TAI = 11;
+pub const CSIGNAL = 0x000000ff;
+pub const CLONE_VM = 0x00000100;
+pub const CLONE_FS = 0x00000200;
+pub const CLONE_FILES = 0x00000400;
+pub const CLONE_SIGHAND = 0x00000800;
+pub const CLONE_PTRACE = 0x00002000;
+pub const CLONE_VFORK = 0x00004000;
+pub const CLONE_PARENT = 0x00008000;
+pub const CLONE_THREAD = 0x00010000;
+pub const CLONE_NEWNS = 0x00020000;
+pub const CLONE_SYSVSEM = 0x00040000;
+pub const CLONE_SETTLS = 0x00080000;
+pub const CLONE_PARENT_SETTID = 0x00100000;
+pub const CLONE_CHILD_CLEARTID = 0x00200000;
+pub const CLONE_DETACHED = 0x00400000;
+pub const CLONE_UNTRACED = 0x00800000;
+pub const CLONE_CHILD_SETTID = 0x01000000;
+pub const CLONE_NEWCGROUP = 0x02000000;
+pub const CLONE_NEWUTS = 0x04000000;
+pub const CLONE_NEWIPC = 0x08000000;
+pub const CLONE_NEWUSER = 0x10000000;
+pub const CLONE_NEWPID = 0x20000000;
+pub const CLONE_NEWNET = 0x40000000;
+pub const CLONE_IO = 0x80000000;
+
+pub const MS_RDONLY = 1;
+pub const MS_NOSUID = 2;
+pub const MS_NODEV = 4;
+pub const MS_NOEXEC = 8;
+pub const MS_SYNCHRONOUS = 16;
+pub const MS_REMOUNT = 32;
+pub const MS_MANDLOCK = 64;
+pub const MS_DIRSYNC = 128;
+pub const MS_NOATIME = 1024;
+pub const MS_NODIRATIME = 2048;
+pub const MS_BIND = 4096;
+pub const MS_MOVE = 8192;
+pub const MS_REC = 16384;
+pub const MS_SILENT = 32768;
+pub const MS_POSIXACL = (1<<16);
+pub const MS_UNBINDABLE = (1<<17);
+pub const MS_PRIVATE = (1<<18);
+pub const MS_SLAVE = (1<<19);
+pub const MS_SHARED = (1<<20);
+pub const MS_RELATIME = (1<<21);
+pub const MS_KERNMOUNT = (1<<22);
+pub const MS_I_VERSION = (1<<23);
+pub const MS_STRICTATIME = (1<<24);
+pub const MS_LAZYTIME = (1<<25);
+pub const MS_NOREMOTELOCK = (1<<27);
+pub const MS_NOSEC = (1<<28);
+pub const MS_BORN = (1<<29);
+pub const MS_ACTIVE = (1<<30);
+pub const MS_NOUSER = (1<<31);
+
+pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME);
+
+pub const MS_MGC_VAL = 0xc0ed0000;
+pub const MS_MGC_MSK = 0xffff0000;
+
+pub const MNT_FORCE = 1;
+pub const MNT_DETACH = 2;
+pub const MNT_EXPIRE = 4;
+pub const UMOUNT_NOFOLLOW = 8;
+
+
+pub const S_IFMT = 0o170000;
+
+pub const S_IFDIR = 0o040000;
+pub const S_IFCHR = 0o020000;
+pub const S_IFBLK = 0o060000;
+pub const S_IFREG = 0o100000;
+pub const S_IFIFO = 0o010000;
+pub const S_IFLNK = 0o120000;
+pub const S_IFSOCK = 0o140000;
+
+pub const S_ISUID = 0o4000;
+pub const S_ISGID = 0o2000;
+pub const S_ISVTX = 0o1000;
+pub const S_IRUSR = 0o400;
+pub const S_IWUSR = 0o200;
+pub const S_IXUSR = 0o100;
+pub const S_IRWXU = 0o700;
+pub const S_IRGRP = 0o040;
+pub const S_IWGRP = 0o020;
+pub const S_IXGRP = 0o010;
+pub const S_IRWXG = 0o070;
+pub const S_IROTH = 0o004;
+pub const S_IWOTH = 0o002;
+pub const S_IXOTH = 0o001;
+pub const S_IRWXO = 0o007;
+
+pub fn S_ISREG(m: u32) bool {
+ return m & S_IFMT == S_IFREG;
+}
+
+pub fn S_ISDIR(m: u32) bool {
+ return m & S_IFMT == S_IFDIR;
+}
+
+pub fn S_ISCHR(m: u32) bool {
+ return m & S_IFMT == S_IFCHR;
+}
+
+pub fn S_ISBLK(m: u32) bool {
+ return m & S_IFMT == S_IFBLK;
+}
+
+pub fn S_ISFIFO(m: u32) bool {
+ return m & S_IFMT == S_IFIFO;
+}
+
+pub fn S_ISLNK(m: u32) bool {
+ return m & S_IFMT == S_IFLNK;
+}
+
+pub fn S_ISSOCK(m: u32) bool {
+ return m & S_IFMT == S_IFSOCK;
+}
+
pub const TFD_NONBLOCK = O_NONBLOCK;
pub const TFD_CLOEXEC = O_CLOEXEC;
@@ -380,6 +657,10 @@ pub fn chdir(path: &const u8) usize {
return syscall1(SYS_chdir, @ptrToInt(path));
}
+pub fn chroot(path: &const u8) usize {
+ return syscall1(SYS_chroot, @ptrToInt(path));
+}
+
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {
return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
}
@@ -388,6 +669,10 @@ pub fn fork() usize {
return syscall0(SYS_fork);
}
+pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?&timespec) usize {
+ return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
+}
+
pub fn getcwd(buf: &u8, size: usize) usize {
return syscall2(SYS_getcwd, @ptrToInt(buf), size);
}
@@ -409,13 +694,25 @@ pub fn mkdir(path: &const u8, mode: u32) usize {
return syscall2(SYS_mkdir, @ptrToInt(path), mode);
}
-pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
+pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize {
+ return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
+}
+
+pub fn umount(special: &const u8) usize {
+ return syscall2(SYS_umount2, @ptrToInt(special), 0);
+}
+
+pub fn umount2(special: &const u8, flags: u32) usize {
+ return syscall2(SYS_umount2, @ptrToInt(special), flags);
+}
+
+pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
@bitCast(usize, offset));
}
-pub fn munmap(address: &u8, length: usize) usize {
- return syscall2(SYS_munmap, @ptrToInt(address), length);
+pub fn munmap(address: usize, length: usize) usize {
+ return syscall2(SYS_munmap, address, length);
}
pub fn read(fd: i32, buf: &u8, count: usize) usize {
@@ -434,6 +731,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
}
+pub fn access(path: &const u8, mode: u32) usize {
+ return syscall2(SYS_access, @ptrToInt(path), mode);
+}
+
pub fn pipe(fd: &[2]i32) usize {
return pipe2(fd, 0);
}
@@ -466,6 +767,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {
return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
}
+/// See also `clone` (from the arch-specific include)
+pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize {
+ return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls);
+}
+
+/// See also `clone` (from the arch-specific include)
+pub fn clone2(flags: usize, child_stack_ptr: usize) usize {
+ return syscall2(SYS_clone, flags, child_stack_ptr);
+}
+
pub fn close(fd: i32) usize {
return syscall1(SYS_close, usize(fd));
}
@@ -495,6 +806,45 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
}
+pub fn clock_gettime(clk_id: i32, tp: &timespec) usize {
+ if (VDSO_CGT_SYM.len != 0) {
+ const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered);
+ if (@ptrToInt(f) != 0) {
+ const rc = f(clk_id, tp);
+ switch (rc) {
+ 0, @bitCast(usize, isize(-EINVAL)) => return rc,
+ else => {},
+ }
+ }
+ }
+ return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
+}
+var vdso_clock_gettime = init_vdso_clock_gettime;
+extern fn init_vdso_clock_gettime(clk: i32, ts: &timespec) usize {
+ const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM);
+ var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr);
+ _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f,
+ builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic);
+ if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS));
+ return f(clk, ts);
+}
+
+pub fn clock_getres(clk_id: i32, tp: &timespec) usize {
+ return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
+}
+
+pub fn clock_settime(clk_id: i32, tp: &const timespec) usize {
+ return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
+}
+
+pub fn gettimeofday(tv: &timeval, tz: &timezone) usize {
+ return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
+}
+
+pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize {
+ return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
+}
+
pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {
return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
}
@@ -515,6 +865,58 @@ pub fn setregid(rgid: u32, egid: u32) usize {
return syscall2(SYS_setregid, rgid, egid);
}
+pub fn getuid() u32 {
+ return u32(syscall0(SYS_getuid));
+}
+
+pub fn getgid() u32 {
+ return u32(syscall0(SYS_getgid));
+}
+
+pub fn geteuid() u32 {
+ return u32(syscall0(SYS_geteuid));
+}
+
+pub fn getegid() u32 {
+ return u32(syscall0(SYS_getegid));
+}
+
+pub fn seteuid(euid: u32) usize {
+ return syscall1(SYS_seteuid, euid);
+}
+
+pub fn setegid(egid: u32) usize {
+ return syscall1(SYS_setegid, egid);
+}
+
+pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize {
+ return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
+}
+
+pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize {
+ return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
+}
+
+pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
+ return syscall3(SYS_setresuid, ruid, euid, suid);
+}
+
+pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
+ return syscall3(SYS_setresgid, rgid, egid, sgid);
+}
+
+pub fn getgroups(size: usize, list: &u32) usize {
+ return syscall2(SYS_getgroups, size, @ptrToInt(list));
+}
+
+pub fn setgroups(size: usize, list: &const u32) usize {
+ return syscall2(SYS_setgroups, size, @ptrToInt(list));
+}
+
+pub fn getpid() i32 {
+ return @bitCast(i32, u32(syscall0(SYS_getpid)));
+}
+
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {
return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
}
@@ -599,30 +1001,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool {
return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0;
}
-
+pub const in_port_t = u16;
pub const sa_family_t = u16;
pub const socklen_t = u32;
-pub const in_addr = u32;
-pub const in6_addr = [16]u8;
-pub const sockaddr = extern struct {
- family: sa_family_t,
- port: u16,
- data: [12]u8,
+pub const sockaddr = extern union {
+ in: sockaddr_in,
+ in6: sockaddr_in6,
};
pub const sockaddr_in = extern struct {
family: sa_family_t,
- port: u16,
- addr: in_addr,
+ port: in_port_t,
+ addr: u32,
zero: [8]u8,
};
pub const sockaddr_in6 = extern struct {
family: sa_family_t,
- port: u16,
+ port: in_port_t,
flowinfo: u32,
- addr: in6_addr,
+ addr: [16]u8,
scope_id: u32,
};
@@ -639,16 +1038,16 @@ pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) us
return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
-pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize {
- return syscall3(SYS_socket, usize(domain), usize(socket_type), usize(protocol));
+pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
+ return syscall3(SYS_socket, domain, socket_type, protocol);
}
-pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize {
- return syscall5(SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
+pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize {
+ return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen));
}
-pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
- return syscall5(SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
+pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
+ return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
}
pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {
@@ -677,8 +1076,8 @@ pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
}
-pub fn listen(fd: i32, backlog: i32) usize {
- return syscall2(SYS_listen, usize(fd), usize(backlog));
+pub fn listen(fd: i32, backlog: u32) usize {
+ return syscall2(SYS_listen, usize(fd), backlog);
}
pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize {
@@ -697,46 +1096,83 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
}
-// error NameTooLong;
-// error SystemResources;
-// error Io;
-//
-// pub fn if_nametoindex(name: []u8) !u32 {
-// var ifr: ifreq = undefined;
-//
-// if (name.len >= ifr.ifr_name.len) {
-// return error.NameTooLong;
-// }
-//
-// const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-// const socket_err = getErrno(socket_ret);
-// if (socket_err > 0) {
-// return error.SystemResources;
-// }
-// const socket_fd = i32(socket_ret);
-// @memcpy(&ifr.ifr_name[0], &name[0], name.len);
-// ifr.ifr_name[name.len] = 0;
-// const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
-// close(socket_fd);
-// const ioctl_err = getErrno(ioctl_ret);
-// if (ioctl_err > 0) {
-// return error.Io;
-// }
-// return ifr.ifr_ifindex;
-// }
-
pub fn fstat(fd: i32, stat_buf: &Stat) usize {
return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
}
-pub const epoll_data = extern union {
+pub fn stat(pathname: &const u8, statbuf: &Stat) usize {
+ return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
+}
+
+pub fn lstat(pathname: &const u8, statbuf: &Stat) usize {
+ return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
+}
+
+pub fn listxattr(path: &const u8, list: &u8, size: usize) usize {
+ return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
+}
+
+pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize {
+ return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
+}
+
+pub fn flistxattr(fd: usize, list: &u8, size: usize) usize {
+ return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
+}
+
+pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn setxattr(path: &const u8, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn fsetxattr(fd: usize, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn removexattr(path: &const u8, name: &const u8) usize {
+ return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
+}
+
+pub fn lremovexattr(path: &const u8, name: &const u8) usize {
+ return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
+}
+
+pub fn fremovexattr(fd: usize, name: &const u8) usize {
+ return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
+}
+
+pub const epoll_data = packed union {
ptr: usize,
fd: i32,
@"u32": u32,
@"u64": u64,
};
-pub const epoll_event = extern struct {
+pub const epoll_event = packed struct {
events: u32,
data: epoll_data,
};
@@ -749,7 +1185,7 @@ pub fn epoll_create1(flags: usize) usize {
return syscall1(SYS_epoll_create1, flags);
}
-pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
+pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize {
return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
}
@@ -774,9 +1210,126 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va
return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
}
-test "import linux test" {
- // TODO lazy analysis should prevent this test from being compiled on windows, but
- // it is still compiled on windows
+pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330;
+pub const _LINUX_CAPABILITY_U32S_1 = 1;
+
+pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026;
+pub const _LINUX_CAPABILITY_U32S_2 = 2;
+
+pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522;
+pub const _LINUX_CAPABILITY_U32S_3 = 2;
+
+pub const VFS_CAP_REVISION_MASK = 0xFF000000;
+pub const VFS_CAP_REVISION_SHIFT = 24;
+pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK;
+pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001;
+
+pub const VFS_CAP_REVISION_1 = 0x01000000;
+pub const VFS_CAP_U32_1 = 1;
+pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1);
+
+pub const VFS_CAP_REVISION_2 = 0x02000000;
+pub const VFS_CAP_U32_2 = 2;
+pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2);
+
+pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2;
+pub const VFS_CAP_U32 = VFS_CAP_U32_2;
+pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2;
+
+pub const vfs_cap_data = extern struct {
+ //all of these are mandated as little endian
+ //when on disk.
+ const Data = struct {
+ permitted: u32,
+ inheritable: u32,
+ };
+
+ magic_etc: u32,
+ data: [VFS_CAP_U32]Data,
+};
+
+
+pub const CAP_CHOWN = 0;
+pub const CAP_DAC_OVERRIDE = 1;
+pub const CAP_DAC_READ_SEARCH = 2;
+pub const CAP_FOWNER = 3;
+pub const CAP_FSETID = 4;
+pub const CAP_KILL = 5;
+pub const CAP_SETGID = 6;
+pub const CAP_SETUID = 7;
+pub const CAP_SETPCAP = 8;
+pub const CAP_LINUX_IMMUTABLE = 9;
+pub const CAP_NET_BIND_SERVICE = 10;
+pub const CAP_NET_BROADCAST = 11;
+pub const CAP_NET_ADMIN = 12;
+pub const CAP_NET_RAW = 13;
+pub const CAP_IPC_LOCK = 14;
+pub const CAP_IPC_OWNER = 15;
+pub const CAP_SYS_MODULE = 16;
+pub const CAP_SYS_RAWIO = 17;
+pub const CAP_SYS_CHROOT = 18;
+pub const CAP_SYS_PTRACE = 19;
+pub const CAP_SYS_PACCT = 20;
+pub const CAP_SYS_ADMIN = 21;
+pub const CAP_SYS_BOOT = 22;
+pub const CAP_SYS_NICE = 23;
+pub const CAP_SYS_RESOURCE = 24;
+pub const CAP_SYS_TIME = 25;
+pub const CAP_SYS_TTY_CONFIG = 26;
+pub const CAP_MKNOD = 27;
+pub const CAP_LEASE = 28;
+pub const CAP_AUDIT_WRITE = 29;
+pub const CAP_AUDIT_CONTROL = 30;
+pub const CAP_SETFCAP = 31;
+pub const CAP_MAC_OVERRIDE = 32;
+pub const CAP_MAC_ADMIN = 33;
+pub const CAP_SYSLOG = 34;
+pub const CAP_WAKE_ALARM = 35;
+pub const CAP_BLOCK_SUSPEND = 36;
+pub const CAP_AUDIT_READ = 37;
+pub const CAP_LAST_CAP = CAP_AUDIT_READ;
+
+pub fn cap_valid(u8: x) bool {
+ return x >= 0 and x <= CAP_LAST_CAP;
+}
+
+pub fn CAP_TO_MASK(cap: u8) u32 {
+ return u32(1) << u5(cap & 31);
+}
+
+pub fn CAP_TO_INDEX(cap: u8) u8 {
+ return cap >> 5;
+}
+
+pub const cap_t = extern struct {
+ hdrp: &cap_user_header_t,
+ datap: &cap_user_data_t,
+};
+
+pub const cap_user_header_t = extern struct {
+ version: u32,
+ pid: usize,
+};
+
+pub const cap_user_data_t = extern struct {
+ effective: u32,
+ permitted: u32,
+ inheritable: u32,
+};
+
+pub fn unshare(flags: usize) usize {
+ return syscall1(SYS_unshare, usize(flags));
+}
+
+pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize {
+ return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
+}
+
+pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
+ return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
+}
+
+test "import" {
if (builtin.os == builtin.Os.linux) {
_ = @import("test.zig");
}
diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig
index e427fd5d59..18a6e5f19f 100644
--- a/std/os/linux/test.zig
+++ b/std/os/linux/test.zig
@@ -1,4 +1,5 @@
const std = @import("../../index.zig");
+const builtin = @import("builtin");
const linux = std.os.linux;
const assert = std.debug.assert;
diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig
new file mode 100644
index 0000000000..f4fb513af9
--- /dev/null
+++ b/std/os/linux/vdso.zig
@@ -0,0 +1,89 @@
+const std = @import("../../index.zig");
+const elf = std.elf;
+const linux = std.os.linux;
+const cstr = std.cstr;
+const mem = std.mem;
+
+pub fn lookup(vername: []const u8, name: []const u8) usize {
+ const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR];
+ if (vdso_addr == 0) return 0;
+
+ const eh = @intToPtr(&elf.Ehdr, vdso_addr);
+ var ph_addr: usize = vdso_addr + eh.e_phoff;
+ const ph = @intToPtr(&elf.Phdr, ph_addr);
+
+ var maybe_dynv: ?&usize = null;
+ var base: usize = @maxValue(usize);
+ {
+ var i: usize = 0;
+ while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) {
+ const this_ph = @intToPtr(&elf.Phdr, ph_addr);
+ switch (this_ph.p_type) {
+ elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr,
+ elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset),
+ else => {},
+ }
+ }
+ }
+ const dynv = maybe_dynv ?? return 0;
+ if (base == @maxValue(usize)) return 0;
+
+ var maybe_strings: ?&u8 = null;
+ var maybe_syms: ?&elf.Sym = null;
+ var maybe_hashtab: ?&linux.Elf_Symndx = null;
+ var maybe_versym: ?&u16 = null;
+ var maybe_verdef: ?&elf.Verdef = null;
+
+ {
+ var i: usize = 0;
+ while (dynv[i] != 0) : (i += 2) {
+ const p = base + dynv[i + 1];
+ switch (dynv[i]) {
+ elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p),
+ elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p),
+ elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p),
+ elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p),
+ elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p),
+ else => {},
+ }
+ }
+ }
+
+ const strings = maybe_strings ?? return 0;
+ const syms = maybe_syms ?? return 0;
+ const hashtab = maybe_hashtab ?? return 0;
+ if (maybe_verdef == null) maybe_versym = null;
+
+
+ const OK_TYPES = (1<<elf.STT_NOTYPE | 1<<elf.STT_OBJECT | 1<<elf.STT_FUNC | 1<<elf.STT_COMMON);
+ const OK_BINDS = (1<<elf.STB_GLOBAL | 1<<elf.STB_WEAK | 1<<elf.STB_GNU_UNIQUE);
+
+ var i: usize = 0;
+ while (i < hashtab[1]) : (i += 1) {
+ if (0==(u32(1)<<u5(syms[i].st_info&0xf) & OK_TYPES)) continue;
+ if (0==(u32(1)<<u5(syms[i].st_info>>4) & OK_BINDS)) continue;
+ if (0==syms[i].st_shndx) continue;
+ if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue;
+ if (maybe_versym) |versym| {
+ if (!checkver(??maybe_verdef, versym[i], vername, strings))
+ continue;
+ }
+ return base + syms[i].st_value;
+ }
+
+ return 0;
+}
+
+fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool {
+ var def = def_arg;
+ const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
+ while (true) {
+ if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
+ break;
+ if (def.vd_next == 0)
+ return false;
+ def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next);
+ }
+ const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux);
+ return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name]));
+}
diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig
index cfb2231df9..544b2365ce 100644
--- a/std/os/linux/x86_64.zig
+++ b/std/os/linux/x86_64.zig
@@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16;
pub const F_GETOWNER_UIDS = 17;
+
+pub const VDSO_USEFUL = true;
+pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
+pub const VDSO_CGT_VER = "LINUX_2.6";
+pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
+pub const VDSO_GETCPU_VER = "LINUX_2.6";
+
pub fn syscall0(number: usize) usize {
return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
@@ -443,6 +450,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
: "rcx", "r11");
}
+/// This matches the libc clone function.
+pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize;
+
pub nakedcc fn restore_rt() void {
return asm volatile ("syscall"
:
@@ -489,6 +499,16 @@ pub const timespec = extern struct {
tv_nsec: isize,
};
+pub const timeval = extern struct {
+ tv_sec: isize,
+ tv_usec: isize,
+};
+
+pub const timezone = extern struct {
+ tz_minuteswest: i32,
+ tz_dsttime: i32,
+};
+
pub const dirent = extern struct {
d_ino: usize,
d_off: usize,
@@ -496,3 +516,4 @@ pub const dirent = extern struct {
d_name: u8, // field address is the address of first byte of name
};
+pub const Elf_Symndx = u32;
diff --git a/std/os/test.zig b/std/os/test.zig
index 9c718d5b6b..56d6e8b309 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -6,6 +6,8 @@ const io = std.io;
const a = std.debug.global_allocator;
const builtin = @import("builtin");
+const AtomicRmwOp = builtin.AtomicRmwOp;
+const AtomicOrder = builtin.AtomicOrder;
test "makePath, put some files in it, deleteTree" {
if (builtin.os == builtin.Os.windows) {
@@ -23,3 +25,45 @@ test "makePath, put some files in it, deleteTree" {
assert(err == error.PathNotFound);
}
}
+
+test "access file" {
+ if (builtin.os == builtin.Os.windows) {
+ return;
+ }
+
+ try os.makePath(a, "os_test_tmp");
+ if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
+ unreachable;
+ } else |err| {
+ assert(err == error.NotFound);
+ }
+
+ try io.writeFile(a, "os_test_tmp/file.txt", "");
+ assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
+ try os.deleteTree(a, "os_test_tmp");
+}
+
+test "spawn threads" {
+ var shared_ctx: i32 = 1;
+
+ const thread1 = try std.os.spawnThread({}, start1);
+ const thread2 = try std.os.spawnThread(&shared_ctx, start2);
+ const thread3 = try std.os.spawnThread(&shared_ctx, start2);
+ const thread4 = try std.os.spawnThread(&shared_ctx, start2);
+
+ thread1.wait();
+ thread2.wait();
+ thread3.wait();
+ thread4.wait();
+
+ assert(shared_ctx == 4);
+}
+
+fn start1(ctx: void) u8 {
+ return 0;
+}
+
+fn start2(ctx: &i32) u8 {
+ _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+ return 0;
+}
diff --git a/std/os/time.zig b/std/os/time.zig
new file mode 100644
index 0000000000..4fd2c4e924
--- /dev/null
+++ b/std/os/time.zig
@@ -0,0 +1,288 @@
+const std = @import("../index.zig");
+const builtin = @import("builtin");
+const Os = builtin.Os;
+const debug = std.debug;
+
+const windows = std.os.windows;
+const linux = std.os.linux;
+const darwin = std.os.darwin;
+const posix = std.os.posix;
+
+pub const epoch = @import("epoch.zig");
+
+/// Sleep for the specified duration
+pub fn sleep(seconds: usize, nanoseconds: usize) void {
+ switch (builtin.os) {
+ Os.linux, Os.macosx, Os.ios => {
+ posixSleep(u63(seconds), u63(nanoseconds));
+ },
+ Os.windows => {
+ const ns_per_ms = ns_per_s / ms_per_s;
+ const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms;
+ windows.Sleep(windows.DWORD(milliseconds));
+ },
+ else => @compileError("Unsupported OS"),
+ }
+}
+
+const u63 = @IntType(false, 63);
+pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
+ var req = posix.timespec {
+ .tv_sec = seconds,
+ .tv_nsec = nanoseconds,
+ };
+ var rem: posix.timespec = undefined;
+ while (true) {
+ const ret_val = posix.nanosleep(&req, &rem);
+ const err = posix.getErrno(ret_val);
+ if (err == 0) return;
+ switch (err) {
+ posix.EFAULT => unreachable,
+ posix.EINVAL => {
+ // Sometimes Darwin returns EINVAL for no reason.
+ // We treat it as a spurious wakeup.
+ return;
+ },
+ posix.EINTR => {
+ req = rem;
+ continue;
+ },
+ else => return,
+ }
+ }
+}
+
+/// Get the posix timestamp, UTC, in seconds
+pub fn timestamp() u64 {
+ return @divFloor(milliTimestamp(), ms_per_s);
+}
+
+/// Get the posix timestamp, UTC, in milliseconds
+pub const milliTimestamp = switch (builtin.os) {
+ Os.windows => milliTimestampWindows,
+ Os.linux => milliTimestampPosix,
+ Os.macosx, Os.ios => milliTimestampDarwin,
+ else => @compileError("Unsupported OS"),
+};
+
+fn milliTimestampWindows() u64 {
+ //FileTime has a granularity of 100 nanoseconds
+ // and uses the NTFS/Windows epoch
+ var ft: i64 = undefined;
+ windows.GetSystemTimeAsFileTime(&ft);
+ const hns_per_ms = (ns_per_s / 100) / ms_per_s;
+ const epoch_adj = epoch.windows * ms_per_s;
+ return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
+}
+
+fn milliTimestampDarwin() u64 {
+ //Sources suggest MacOS 10.12 has support for
+ // posix clock_gettime.
+ var tv: darwin.timeval = undefined;
+ var err = darwin.gettimeofday(&tv, null);
+ debug.assert(err == 0);
+ const sec_ms = u64(tv.tv_sec) * ms_per_s;
+ const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s);
+ return u64(sec_ms) + u64(usec_ms);
+}
+
+fn milliTimestampPosix() u64 {
+ //From what I can tell there's no reason clock_gettime
+ // should ever fail for us with CLOCK_REALTIME,
+ // seccomp aside.
+ var ts: posix.timespec = undefined;
+ const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts);
+ debug.assert(err == 0);
+ const sec_ms = u64(ts.tv_sec) * ms_per_s;
+ const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s);
+ return sec_ms + nsec_ms;
+}
+
+/// Divisions of a second
+pub const ns_per_s = 1000000000;
+pub const us_per_s = 1000000;
+pub const ms_per_s = 1000;
+pub const cs_per_s = 100;
+
+/// Common time divisions
+pub const s_per_min = 60;
+pub const s_per_hour = s_per_min * 60;
+pub const s_per_day = s_per_hour * 24;
+pub const s_per_week = s_per_day * 7;
+
+
+/// A monotonic high-performance timer.
+/// Timer.start() must be called to initialize the struct, which captures
+/// the counter frequency on windows and darwin, records the resolution,
+/// and gives the user an oportunity to check for the existnece of
+/// monotonic clocks without forcing them to check for error on each read.
+/// .resolution is in nanoseconds on all platforms but .start_time's meaning
+/// depends on the OS. On Windows and Darwin it is a hardware counter
+/// value that requires calculation to convert to a meaninful unit.
+pub const Timer = struct {
+
+ //if we used resolution's value when performing the
+ // performance counter calc on windows/darwin, it would
+ // be less precise
+ frequency: switch (builtin.os) {
+ Os.windows => u64,
+ Os.macosx, Os.ios => darwin.mach_timebase_info_data,
+ else => void,
+ },
+ resolution: u64,
+ start_time: u64,
+
+
+ //At some point we may change our minds on RAW, but for now we're
+ // sticking with posix standard MONOTONIC. For more information, see:
+ // https://github.com/zig-lang/zig/pull/933
+ //
+ //const monotonic_clock_id = switch(builtin.os) {
+ // Os.linux => linux.CLOCK_MONOTONIC_RAW,
+ // else => posix.CLOCK_MONOTONIC,
+ //};
+ const monotonic_clock_id = posix.CLOCK_MONOTONIC;
+
+
+ /// Initialize the timer structure.
+ //This gives us an oportunity to grab the counter frequency in windows.
+ //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
+ //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
+ // supported, or if the timespec pointer is out of bounds, which should be
+ // impossible here barring cosmic rays or other such occurances of
+ // incredibly bad luck.
+ //On Darwin: This cannot fail, as far as I am able to tell.
+ const TimerError = error{TimerUnsupported, Unexpected};
+ pub fn start() TimerError!Timer {
+ var self: Timer = undefined;
+
+ switch (builtin.os) {
+ Os.windows => {
+ var freq: i64 = undefined;
+ var err = windows.QueryPerformanceFrequency(&freq);
+ if (err == windows.FALSE) return error.TimerUnsupported;
+ self.frequency = u64(freq);
+ self.resolution = @divFloor(ns_per_s, self.frequency);
+
+ var start_time: i64 = undefined;
+ err = windows.QueryPerformanceCounter(&start_time);
+ debug.assert(err != windows.FALSE);
+ self.start_time = u64(start_time);
+ },
+ Os.linux => {
+ //On Linux, seccomp can do arbitrary things to our ability to call
+ // syscalls, including return any errno value it wants and
+ // inconsistently throwing errors. Since we can't account for
+ // abuses of seccomp in a reasonable way, we'll assume that if
+ // seccomp is going to block us it will at least do so consistently
+ var ts: posix.timespec = undefined;
+ var result = posix.clock_getres(monotonic_clock_id, &ts);
+ var errno = posix.getErrno(result);
+ switch (errno) {
+ 0 => {},
+ posix.EINVAL => return error.TimerUnsupported,
+ else => return std.os.unexpectedErrorPosix(errno),
+ }
+ self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
+
+ result = posix.clock_gettime(monotonic_clock_id, &ts);
+ errno = posix.getErrno(result);
+ if (errno != 0) return std.os.unexpectedErrorPosix(errno);
+ self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
+ },
+ Os.macosx, Os.ios => {
+ darwin.mach_timebase_info(&self.frequency);
+ self.resolution = @divFloor(self.frequency.numer, self.frequency.denom);
+ self.start_time = darwin.mach_absolute_time();
+ },
+ else => @compileError("Unsupported OS"),
+ }
+ return self;
+ }
+
+ /// Reads the timer value since start or the last reset in nanoseconds
+ pub fn read(self: &Timer) u64 {
+ var clock = clockNative() - self.start_time;
+ return switch (builtin.os) {
+ Os.windows => @divFloor(clock * ns_per_s, self.frequency),
+ Os.linux => clock,
+ Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom),
+ else => @compileError("Unsupported OS"),
+ };
+ }
+
+ /// Resets the timer value to 0/now.
+ pub fn reset(self: &Timer) void
+ {
+ self.start_time = clockNative();
+ }
+
+ /// Returns the current value of the timer in nanoseconds, then resets it
+ pub fn lap(self: &Timer) u64 {
+ var now = clockNative();
+ var lap_time = self.read();
+ self.start_time = now;
+ return lap_time;
+ }
+
+
+ const clockNative = switch (builtin.os) {
+ Os.windows => clockWindows,
+ Os.linux => clockLinux,
+ Os.macosx, Os.ios => clockDarwin,
+ else => @compileError("Unsupported OS"),
+ };
+
+ fn clockWindows() u64 {
+ var result: i64 = undefined;
+ var err = windows.QueryPerformanceCounter(&result);
+ debug.assert(err != windows.FALSE);
+ return u64(result);
+ }
+
+ fn clockDarwin() u64 {
+ return darwin.mach_absolute_time();
+ }
+
+ fn clockLinux() u64 {
+ var ts: posix.timespec = undefined;
+ var result = posix.clock_gettime(monotonic_clock_id, &ts);
+ debug.assert(posix.getErrno(result) == 0);
+ return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
+ }
+};
+
+
+
+
+
+test "os.time.sleep" {
+ sleep(0, 1);
+}
+
+test "os.time.timestamp" {
+ const ns_per_ms = (ns_per_s / ms_per_s);
+ const margin = 50;
+
+ const time_0 = milliTimestamp();
+ sleep(0, ns_per_ms);
+ const time_1 = milliTimestamp();
+ const interval = time_1 - time_0;
+ debug.assert(interval > 0 and interval < margin);
+}
+
+test "os.time.Timer" {
+ const ns_per_ms = (ns_per_s / ms_per_s);
+ const margin = ns_per_ms * 50;
+
+ var timer = try Timer.start();
+ sleep(0, 10 * ns_per_ms);
+ const time_0 = timer.read();
+ debug.assert(time_0 > 0 and time_0 < margin);
+
+ const time_1 = timer.lap();
+ debug.assert(time_1 >= time_0);
+
+ timer.reset();
+ debug.assert(timer.read() < time_1);
+}
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index 2709cf2a78..e13ed0f131 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,
dwFlags: DWORD) BOOLEAN;
+
+pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
+
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
@@ -61,6 +64,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
+pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void;
+
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;
@@ -77,6 +82,12 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
dwFlags: DWORD) BOOL;
+
+pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL;
+
+pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL;
+
+pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
@@ -137,6 +148,7 @@ pub const UNICODE = false;
pub const WCHAR = u16;
pub const WORD = u16;
pub const LARGE_INTEGER = i64;
+pub const FILETIME = i64;
pub const TRUE = 1;
pub const FALSE = 0;
@@ -308,3 +320,10 @@ pub const FILE_END = 2;
pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
pub const HEAP_NO_SERIALIZE = 0x00000001;
+
+pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD;
+pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
+
+test "import" {
+ _ = @import("util.zig");
+}