Zig: BYO os should work at the zig level

Created on 13 Jan 2020  路  5Comments  路  Source: ziglang/zig

The "byo os" mechanism currently expects signatures similar to POSIX such as pub fn write(fd: bits.fd_t, ptr: [*]const u8, len: usize) usize and expects an errno-like mechanism; rather than using the zig errors and slices.

os-bring-your-own standard library

Most helpful comment

This proposal needs to be more clear about what exactly it is proposing. As is, it's not clear enough for anyone to do anything about.

In os.zig we do:

pub const system = if (@hasDecl(root, "os") and root.os != @This())
    root.os.system
else if (builtin.link_libc)
    std.c
else switch (builtin.os) {
.....
    .linux => @import("os/linux.zig"),
.....

This means that when you "BYO os", you have to match the signatures in std.os.linux and std.c. The signature of std.os.linux.write is pub fn write(fd: i32, buf: [*]const u8, count: usize) usize; implementing that has all sorts of complexities in terms of stuffing errors into the return value or elsewhere, and providing a root.os.getErrno to match.

This issue is proposing that std.os.linux instead has the signature pub fn write(fd: fd_t, bytes: []const u8) WriteError!void and that when I bring my own OS: I implement that.

All 5 comments

std.os.write has this definition:

pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {

This proposal needs to be more clear about what exactly it is proposing. As is, it's not clear enough for anyone to do anything about. Feel free to elaborate with examples and criteria for closing, and then re-open the issue.

I remember having to implement a fake errno function for these to work. The problem with this is that the BYOS layer fundamentally requires the same signature as the other OS layers, and so there's no good way to provide this improvement without a much larger structural change.

This proposal needs to be more clear about what exactly it is proposing. As is, it's not clear enough for anyone to do anything about.

In os.zig we do:

pub const system = if (@hasDecl(root, "os") and root.os != @This())
    root.os.system
else if (builtin.link_libc)
    std.c
else switch (builtin.os) {
.....
    .linux => @import("os/linux.zig"),
.....

This means that when you "BYO os", you have to match the signatures in std.os.linux and std.c. The signature of std.os.linux.write is pub fn write(fd: i32, buf: [*]const u8, count: usize) usize; implementing that has all sorts of complexities in terms of stuffing errors into the return value or elsewhere, and providing a root.os.getErrno to match.

This issue is proposing that std.os.linux instead has the signature pub fn write(fd: fd_t, bytes: []const u8) WriteError!void and that when I bring my own OS: I implement that.

I propose to solve this that os.zig functions possibly forward their arguments directly. So we keep the root.os.system layer option, which matches other systems, but functions could be directly provided in root.os and get the zig layer. Does that make sense?

So for write, I'm proposing this change:

--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -52,7 +52,7 @@ test "" {
 /// Applications can override the `system` API layer in their root source file.
 /// Otherwise, when linking libc, this is the C API.
 /// When not linking libc, it is the OS-specific system interface.
-pub const system = if (@hasDecl(root, "os") and root.os != @This())
+pub const system = if (have_root_os)
     root.os.system
 else if (builtin.link_libc)
     std.c
@@ -66,6 +66,7 @@ else switch (builtin.os) {
     .windows => windows,
     else => struct {},
 };
+const have_root_os = @hasDecl(root, "os") and root.os != @This();

 pub usingnamespace @import("os/bits.zig");

@@ -454,6 +455,9 @@ pub const WriteError = error{
 /// TODO evented I/O integration is disabled until
 /// https://github.com/ziglang/zig/issues/3557 is solved.
 pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
+    if (have_root_os and @hasDecl(root.os, "write")) {
+        return root.os.write(fd, bytes);
+    }
     if (builtin.os == .windows) {
         return windows.WriteFile(fd, bytes);
     }

With similar patches for nearly every std.os function.

+    if (have_root_os and @hasDecl(root.os, "write")) {
+        return root.os.write(fd, bytes);
+    }

I'm not sure I love the idea of every os function having that as a preamble.
Given that its followed by a windows implementation:

     if (builtin.os == .windows) {
         return windows.WriteFile(fd, bytes);
     }

How can we have e.g. windows implement the os interface and dispatch to that? That way windows would be "using the BYO os" layer but obviously not BYO.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andersfr picture andersfr  路  3Comments

dobkeratops picture dobkeratops  路  3Comments

zimmi picture zimmi  路  3Comments

andrewrk picture andrewrk  路  3Comments

bronze1man picture bronze1man  路  3Comments