Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support all operating systems, even hobby OSes, with a "bring your own OS layer" in the std lib #3784

Closed
andrewrk opened this issue Nov 26, 2019 · 2 comments
Labels
accepted This proposal is planned. os-bring-your-own The "Bring Your Own Operating System" abstraction layer proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@andrewrk
Copy link
Member

The std.Target.Os enum recognizes the following operating systems:

  freestanding
  ananas
  cloudabi
  dragonfly
  freebsd
  fuchsia
  ios
  kfreebsd
  linux
  lv2
  macosx
  netbsd
  openbsd
  solaris
  windows
  haiku
  minix
  rtems
  nacl
  cnk
  aix
  cuda
  nvcl
  amdhsa
  ps4
  elfiamcu
  tvos
  watchos
  mesa3d
  contiki
  amdpal
  hermit
  hurd
  wasi
  emscripten
  zen
  uefi

This list is the list of targets that LLVM supports + zen (a hobby OS not maintained for over 1 year) + UEFI.

It doesn't make sense to put every hobby OS into this list, but it does make sense to support them! It should be possible for people to take advantage of Zig's cross platform abstractions without having to get support for their hobby OS upstreamed into Zig.

I propose to do this with 2 things:

  • Add other tag to std.Target.Os
  • Support an OS layer struct exposed in the root source file (next to pub fn main())

This allows hobby OS developers to maintain a zig package that makes the zig standard library support their OS. Application developers could use it like this:

pub const os = @import("my_hobby_os_package");

pub fn main() void {
    // ...
}

Next, standard library abstractions will detect when they should utilize this. If the operating system is POSIX compliant, then many things will Just Work. For example, std.os.read is currently defined like this:

/// Returns the number of bytes that were read, which can be less than
/// buf.len. If 0 bytes were read, that means EOF.
/// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
    if (builtin.os == .windows) {
        return windows.ReadFile(fd, buf);
    }

    if (builtin.os == .wasi and !builtin.link_libc) {
        const iovs = [1]iovec{iovec{
            .iov_base = buf.ptr,
            .iov_len = buf.len,
        }};

        var nread: usize = undefined;
        switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
            0 => return nread,
            else => |err| return unexpectedErrno(err),
        }
    }

    while (true) {
        const rc = system.read(fd, buf.ptr, buf.len);
        switch (errno(rc)) {
            0 => return @intCast(usize, rc),
            EINTR => continue,
            EINVAL => unreachable,
            EFAULT => unreachable,
            EAGAIN => if (std.event.Loop.instance) |loop| {
                loop.waitUntilFdReadable(fd);
                continue;
            } else {
                return error.WouldBlock;
            },
            EBADF => unreachable, // Always a race condition.
            EIO => return error.InputOutput,
            EISDIR => return error.IsDir,
            ENOBUFS => return error.SystemResources,
            ENOMEM => return error.SystemResources,
            ECONNRESET => return error.ConnectionResetByPeer,
            else => |err| return unexpectedErrno(err),
        }
    }
    return index;
}

system in this refers to:

/// When linking libc, this is the C API. Otherwise, it is the OS-specific system interface.
pub const system = if (builtin.link_libc) std.c else switch (builtin.os) {
    .macosx, .ios, .watchos, .tvos => darwin,
    .freebsd => freebsd,
    .linux => linux,
    .netbsd => netbsd,
    .dragonfly => dragonfly,
    .wasi => wasi,
    .windows => windows,
    .zen => zen,
    else => struct {},
};

Here we would change else => struct{}, to look for @import("root").os if it was provided. With this modification, as long as the OS package defined all the constants (such as fd_t and EISDIR), then std.os.write would end up calling the write function from the hobby OS package, and everything Just Works.

Some abstractions may not work so smoothly; in this case there could be code that looks something like this:

fn doTheOsThing() void {
    if (std.builtin.os == .other and @hasDecl(root, "os") and @hasDecl(root.os, "doTheOsThing")) {
        return @import("root").os.doTheOsThing();
    }
}

Really, the check for the "other" OS isn't even necessary. Allowing applications to override fundamental OS functions could be useful on any operating system.

With this proposal implemented, Zig would have very near first class support for all operating systems. The main difference between upstream-recognized OSes and "other" OSes would be where the support is maintained - upstream zig std lib, or in a third party "OS layer" package.

cc @pixelherodev

@andrewrk andrewrk added proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. accepted This proposal is planned. labels Nov 26, 2019
@andrewrk andrewrk added this to the 0.7.0 milestone Nov 26, 2019
@jzck
Copy link
Sponsor

jzck commented Nov 29, 2019

Great idea for hobby OSes,
Could the idea be pushed even further? All operating system integration could be done via third party packages. This way upstream zig std lib wouldn't even have to know about operating system specifics.

@pixelherodev
Copy link
Contributor

In theory, yes. In practice, that's not feasible - at least, not until the package manager is implemented...

I suppose with the package manager, we could extricate the OS backends, but they'd still need to be upstream, and that just makes things harder to manage.

@andrewrk andrewrk added the standard library This issue involves writing Zig code for the standard library. label Dec 2, 2019
andrewrk added a commit that referenced this issue Dec 2, 2019
The new plan to support hobby operating systems is #3784.

And what kind of name is "Zen" anyway? There's already a
[Zen programming language](http://zenlang.sourceforge.net/)
and that's just confusing.
hspak pushed a commit to hspak/zig that referenced this issue Dec 6, 2019
The new plan to support hobby operating systems is ziglang#3784.

And what kind of name is "Zen" anyway? There's already a
[Zen programming language](http://zenlang.sourceforge.net/)
and that's just confusing.
hspak pushed a commit to hspak/zig that referenced this issue Dec 6, 2019
@andrewrk andrewrk modified the milestones: 0.7.0, 0.6.0 Feb 29, 2020
@jayschwa jayschwa added the os-bring-your-own The "Bring Your Own Operating System" abstraction layer label Sep 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. os-bring-your-own The "Bring Your Own Operating System" abstraction layer proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

No branches or pull requests

4 participants