From: Brendan Hansen Date: Wed, 18 Oct 2023 02:36:52 +0000 (-0500) Subject: added: networking and more to wasi platform layer X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=a0f1ee504d0b4c1c93fc93e19fd5b3b54c33e459;p=onyx.git added: networking and more to wasi platform layer --- diff --git a/core/runtime/platform/wasi/platform.onyx b/core/runtime/platform/wasi/platform.onyx index 85dd6f86..3b8ac9a9 100644 --- a/core/runtime/platform/wasi/platform.onyx +++ b/core/runtime/platform/wasi/platform.onyx @@ -32,13 +32,24 @@ Supports_Directories :: true Supports_Os :: true Supports_Processes :: false Supports_Time :: true -Supports_Networking :: false Supports_Type_Info :: true Supports_Threads :: true Supports_Env_Vars :: true -Supports_Futexes :: false -Supports_TTY :: false +#if #defined(runtime.vars.WASIX) { + Supports_Networking :: true + Supports_Futexes :: true + + #load "./wasix_defs" + #load "./wasix_net" + #load "./wasix_misc" + +} else { + Supports_Networking :: false + Supports_Futexes :: false +} + +Supports_TTY :: false __output_string :: (s: str) -> u32 { STDOUT_FILENO :: 1 @@ -64,15 +75,17 @@ __read_from_input :: (buffer: [] u8) -> i32 { } __wait_for_input :: (timeout: i32) -> bool { + STDIN_FILENO :: 0 + events := 1; subscriptions: [2] Subscription; fd_tagged: SubscriptionTagged; fd_tagged.tag = .FDRead; - fd_tagged.fd_read = .{ file_description = STDIN_FILENO, }; + fd_tagged.fd_read = .{ file_descriptor = STDIN_FILENO }; subscriptions[0] = .{ userdata = 0, - u = tagged, + u = fd_tagged, }; clock_tagged: SubscriptionTagged; @@ -82,7 +95,7 @@ __wait_for_input :: (timeout: i32) -> bool { clock_tagged.clock = .{ id = .Realtime, timeout = cast(u64) timeout * 1000000, - precision + precision = 1, }; subscriptions[1] = .{ @@ -94,7 +107,7 @@ __wait_for_input :: (timeout: i32) -> bool { event: [2] Event; number_of_events: u32; - error_code := poll_oneoff(&subscription, &event, 2, &number_of_events); + error_code := poll_oneoff(~~subscriptions, ~~event, 2, &number_of_events); if number_of_events == 0 do return false; if event[0].type == .Clock do return false; @@ -137,6 +150,61 @@ __random_get :: (buf: [] u8) { random_get(buf.data, buf.length); } +PollDescription :: struct { + fd: wasi.FileDescriptor; + in: core.io.PollEvent; + out: core.io.PollEvent; +} + +__poll :: (fds: [] PollDescription, timeout: i32) -> void { + count := fds.count; + if timeout == 0 { + timeout = 1; + } + + if timeout > 0 { + count += 1; + } + + subs := core.alloc.array_from_stack(wasi.Subscription, count); + for i: fds.count { + subs[i].userdata = ~~ i; + subs[i].u.tag = switch fds[i].in { + case .None => .FDRead; + case .Read => .FDRead; + case .Write => .FDWrite; + case .Closed => .FDRead; + }; + subs[i].u.u.fd_read = .{ file_descriptor = cast(i32) fds[i].fd }; + } + + if timeout > 0 { + subs[count - 1].userdata = 0; + subs[count - 1].u.tag = .Clock; + subs[count - 1].u.u.clock = .{ + id = .Realtime, + timeout = cast(u64) timeout * 1000000, + precision = 1, + }; + } + + events := core.alloc.array_from_stack(wasi.Event, count); + number_of_events: u32; + + error_code := wasi.poll_oneoff(~~subs.data, ~~events.data, count, &number_of_events); + if error_code != .Success do return; + + if number_of_events == 0 do return; + for ev: events[0 .. number_of_events] { + if ev.type !=.Clock { + if ev.fd_readwrite.nbytes > 0 { + i := cast(i32) ev.userdata; + fds[i].out = fds[i].in; + } + } + } +} + // Sets up everything needed for execution. __start :: () { diff --git a/core/runtime/platform/wasi/wasi_fs.onyx b/core/runtime/platform/wasi/wasi_fs.onyx index b2589dbd..61a5679e 100644 --- a/core/runtime/platform/wasi/wasi_fs.onyx +++ b/core/runtime/platform/wasi/wasi_fs.onyx @@ -259,44 +259,15 @@ __file_stream_vtable := io.Stream_Vtable.{ return ~~ file_stat.size; }, - poll = (use fs: &os.File, ev: io.PollEvent, timeout: i32) -> (Error, bool) { - // TODO: Not entirely sure this is implemented correctly. - events := 1; - subscriptions: [2] Subscription; - - fd_tagged: SubscriptionTagged; - fd_tagged.tag = .FDRead; - fd_tagged.fd_read = .{ file_description = cast(i64) data, }; - subscriptions[0] = .{ - userdata = 0, - u = tagged, - }; - - clock_tagged: SubscriptionTagged; - if timeout > 0 { - events += 1; - clock_tagged.tag = .Clock; - clock_tagged.clock = .{ - id = .Realtime, - timeout = cast(u64) timeout * 1000000, - precision - }; - - subscriptions[1] = .{ - userdata = 0, - u = clock_tagged, - }; - } - - event: [2] Event; - number_of_events: u32; - - error_code := poll_oneoff(&subscription, &event, 2, &number_of_events); + poll = (use fs: &os.File, ev: io.PollEvent, timeout: i32) -> (io.Error, bool) { + p: [1] PollDescription = .[.{ + fd = data.fd, + in = ev, + }]; - if number_of_events == 0 do return .None, false; - if event[0].type == .Clock do return .None, false; + runtime.platform.__poll(p, timeout); - return .None, event[0].fd_readwrite.nbytes > 0; + return .None, p[0].out == ev; } } diff --git a/core/runtime/platform/wasi/wasix_defs.onyx b/core/runtime/platform/wasi/wasix_defs.onyx new file mode 100644 index 00000000..3a2d3cb7 --- /dev/null +++ b/core/runtime/platform/wasi/wasix_defs.onyx @@ -0,0 +1,179 @@ +package wasi + +#if !#defined(WASIX_VERSION) { + WASIX_VERSION :: "wasix_32v1" +} + +EventFDFlags :: enum { +} + +TTY :: struct { + cols: u32; + rows: u32; + width: u32; + height: u32; + stdin_tty: bool; + stdout_tty: bool; + stderr_tty: bool; + echo: bool; + line_buffered: bool; + line_feeds: bool; +} + +OptionTimestamp :: union #tag_type u8 { + None: void; + Some: Timestamp; +} + +SockStatus :: enum { + Opening; + Opened; + Closed; + Failed; +} + +AddrIPv4 :: struct { + n0, n1, h0, h1: u8; +} + +AddrIPv6 :: struct { + n0, n1, n2, n3, h0, h1, h2, h3: u8; + flow_info1: u16; + flow_info0: u16; + scope_id1: u16; + scope_id0: u16; +} + +Addr :: union #tag_type u8 { + Unspec: struct { + n0: u8; + }; + + Ipv4: AddrIPv4; + + Ipv6: AddrIPv6; + + Unix: [108] u8; +} + +AddrPort :: union #tag_type u8 { + Unspec: struct { + port: u16; + n0: u8; + }; + + Ipv4: struct { + port: u16; + addr: AddrIPv4; + }; + + Ipv6: struct { + port: u16; + addr: AddrIPv6; + }; + + Unix: struct { + path: [108] u8; + } +} + +AddressFamily :: enum { + Unspec; + Inet; + Inet6; + Unix; +} + +SockType :: enum { + Unused; + Stream; + Dgram; + Raw; + Seqpacket; +} + +SockProto :: enum { + IP; + ICMP; + IGMP; + IPIP :: 4; + TCP :: 6; + EGP :: 8; + PUP :: 12; + UDP :: 17; + IDP :: 22; + DCCP :: 33; + IPV6 :: 41; + ICMPV6 :: 58; +} + +SockOption :: enum { + Noop; + ReusePort; + ReuseAddr; + NoDelay; + DontRoute; + OnlyV6; + Broadcast; + MulticastLoopV4; + MulticastLoopV6; + Promiscuous; + Listening; + LastError; + KeepAlive; + Linger; + OOBInline; + RecvBufSize; + SendBufSize; + RecvLowat; + SendLowat; + RecvTimeout; + SendTimeout; + ConnectTimeout; + AcceptTimeout; + TTL; + MulticastTTLV5; + Type; + Proto; +} + +#foreign WASIX_VERSION { + fd_dup :: (fd: FileDescriptor, out: &FileDescriptor) -> Errno --- + fd_event :: (init_val: u64, flags: EventFDFlags, fd: &FileDescriptor) -> Errno --- + fd_pipe :: (fd1, fd2: &FileDescriptor) -> Errno --- + + tty_get :: (state: &TTY) -> Errno --- + tty_set :: (state: &TTY) -> Errno --- + + getcwd :: (path: [&] u8, pathlen: &u32) -> Errno --- + chdir :: (path: cstr) -> Errno --- + + thread_spawn_v2 :: (args: rawptr, tid: &u32) -> Errno --- + thread_sleep :: (duration: Timestamp) -> Errno --- + thread_id :: (tid: &u32) -> Errno --- + thread_join :: (tid: u32) -> Errno --- + thread_parallelism :: (size: &u32) -> Errno --- + + futex_wait :: (futex: &u32, expected: u32, timeout: &OptionTimestamp, out: &bool) -> Errno --- + futex_wake :: (futex: &u32, out: &bool) -> Errno --- + futex_wake_all :: (futex: &u32, out: &bool) -> Errno --- + + sock_status :: (fd: FileDescriptor, out: &SockStatus) -> Errno --- + sock_addr_local :: (fd: FileDescriptor, addr: &AddrPort) -> Errno --- + sock_addr_peer :: (fd: FileDescriptor, addr: &AddrPort) -> Errno --- + sock_open :: (af: AddressFamily, socktype: SockType, sockproto: SockProto, out: &FileDescriptor) -> Errno --- + sock_set_opt_flag :: (fd: FileDescriptor, sockopt: SockOption, flag: bool) -> Errno --- + sock_get_opt_flag :: (fd: FileDescriptor, sockopt: SockOption, flag: &bool) -> Errno --- + sock_set_opt_time :: (fd: FileDescriptor, sockopt: SockOption, flag: &OptionTimestamp) -> Errno --- + sock_get_opt_time :: (fd: FileDescriptor, sockopt: SockOption, flag: &OptionTimestamp) -> Errno --- + sock_set_opt_size :: (fd: FileDescriptor, sockopt: SockOption, flag: u64) -> Errno --- + sock_get_opt_size :: (fd: FileDescriptor, sockopt: SockOption, flag: &u64) -> Errno --- + sock_bind :: (fd: FileDescriptor, addr: &AddrPort) -> Errno --- + sock_listen :: (fd: FileDescriptor, backlog: i32) -> Errno --- + sock_accept_v2 :: (fd: FileDescriptor, flags: FDFlags, out_sock: &FileDescriptor, out_addr: &AddrPort) -> Errno --- + sock_connect :: (fd: FileDescriptor, addr: &AddrPort) -> Errno --- + sock_recv_from :: (fd: FileDescriptor, data: &IOVec, data_len: Size, flags: RIFlags, written: &Size, oflags: &ROFlags, addr: &AddrPort) -> Errno --- + sock_send_to :: (fd: FileDescriptor, data: &IOVec, data_len: Size, flags: SIFlags, addr: &AddrPort, sent: &Size) -> Errno --- + + resolve :: (host: cstr, port: u16, addr: [&] Addr, addr_len: Size, retaddrs: &Size) -> Errno --- +} \ No newline at end of file diff --git a/core/runtime/platform/wasi/wasix_misc.onyx b/core/runtime/platform/wasi/wasix_misc.onyx new file mode 100644 index 00000000..e0d2d72f --- /dev/null +++ b/core/runtime/platform/wasi/wasix_misc.onyx @@ -0,0 +1,27 @@ +package runtime.platform + +use wasi + +__futex_wait :: (addr: rawptr, expected: i32, timeout: i32) -> i32 { + tm: wasi.OptionTimestamp; + if timeout < 0 { + tm = .{ None = .{} }; + } else { + tm = .{ Some = ~~timeout }; + } + + out: bool; + if wasi.futex_wait(addr, expected, &tm, &out) != .Success do return 0; + return 1 if out else 0; +} + +__futex_wake :: (addr: rawptr, maximum: i32) -> i32 { + out: bool; + if (maximum == -1) { + if wasi.futex_wake_all(addr, &out) != .Success do return 0; + } else { + if wasi.futex_wake(addr, &out) != .Success do return 0; + } + + return 1 if out else 0; +} \ No newline at end of file diff --git a/core/runtime/platform/wasi/wasix_net.onyx b/core/runtime/platform/wasi/wasix_net.onyx new file mode 100644 index 00000000..6abcc07c --- /dev/null +++ b/core/runtime/platform/wasi/wasix_net.onyx @@ -0,0 +1,221 @@ +package runtime.platform + +use wasi +use core.net { + SocketFamily, + SocketProto, + SocketType, + SocketOption, + SocketAddress, + SocketShutdown, + SocketStatus +} +use core {Result, string, io} + +SocketData :: wasi.FileDescriptor + +__net_sock_create :: (af: SocketFamily, type: SocketType, proto: SocketProto) -> Result(SocketData, io.Error) { + family := cast(wasi.AddressFamily) cast(u32) af; + socktype := switch type { + case .Stream => wasi.SockType.Stream; + case .Dgram => wasi.SockType.Dgram; + }; + sockproto := cast(wasi.SockProto) cast(u32) proto; + + sock: wasi.FileDescriptor; + err := wasi.sock_open(family, socktype, sockproto, &sock); + + if err == .Success { + return .{ Ok = sock }; + } + + return .{ Err = .CreationFailed }; +} + +__net_sock_status :: (s: SocketData) -> SocketStatus { + status: wasi.SockStatus; + if wasi.sock_status(s, &status) != .Success { + return .Errored; + } + + return switch status { + case .Opening => .Opening; + case .Opened => .Open; + case .Closed => .Closed; + case .Failed => .Errored; + }; +} + +__net_sock_opt_flag :: (s: SocketData, sockopt: SocketOption, flag: bool) -> bool { + if sockopt == .NonBlocking { + return wasi.fd_fdstat_set_flags(s, wasi.FDFlags.NonBlock if flag else 0) == .Success; + } + + opt := switch sockopt { + case .Broadcast => wasi.SockOption.Broadcast; + case .ReuseAddress => wasi.SockOption.ReuseAddr; + case #default => wasi.SockOption.Noop; + }; + return wasi.sock_set_opt_flag(s, opt, flag) == .Success; +} + +__net_sock_opt_time :: (s: SocketData, sockopt: SocketOption, time: ? u64) -> bool { + return false; +} + +__net_sock_opt_size :: (s: SocketData, sockopt: SocketOption, size: i64) -> bool { + return false; +} + +__net_sock_bind :: (s: SocketData, addr: &SocketAddress) -> bool { + target: wasi.AddrPort; + socket_addr_to_wasix_addr(addr, &target); + + return wasi.sock_bind(s, &target) == .Success; +} + +__net_sock_listen :: (s: SocketData, backlog: i32) -> bool { + return wasi.sock_listen(s, backlog) == .Success; +} + +__net_sock_accept :: (s: SocketData, out: &SocketAddress) -> Result(SocketData, io.Error) { + new_sock: SocketData; + addr: wasi.AddrPort; + switch wasi.sock_accept_v2(s, 0, &new_sock, &addr) { + case .Success --- + case .Again do return .{ Err = .NoData }; + case #default do return .{ Err = .OperationFailed }; + } + + wasi_addr_to_socket_address(&addr, out); + + return .{ Ok = new_sock }; +} + +__net_sock_connect :: (s: SocketData, addr: &SocketAddress) -> io.Error { + target: wasi.AddrPort; + socket_addr_to_wasix_addr(addr, &target); + + return switch wasi.sock_connect(s, &target) { + case .Success => .None; + case .ConnRefused, + .ConnAborted, + .ConnReset + => .ConnectFailed; + + case #default => .OperationFailed; + }; +} + +__net_sock_recv_from :: (s: SocketData, buf: [] u8, out: &SocketAddress) -> Result(i32, io.Error) { + vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count }; + + out_len: u32; + out_flags: wasi.ROFlags; + sender: wasi.AddrPort; + return switch wasi.sock_recv_from(s, &vec, 1, 0, &out_len, &out_flags, &sender) { + case .Success => do { + wasi_addr_to_socket_address(&sender, out); + return Result(i32, io.Error).{ Ok = out_len }; + }; + case .Again => .{ Err = .NoData }; + case #default => .{ Err = .EOF }; + }; +} + +__net_sock_send_to :: (s: SocketData, buf: [] u8, addr: &SocketAddress) -> Result(i32, io.Error) { + vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count }; + + out_len: u32; + target: wasi.AddrPort; + socket_addr_to_wasix_addr(addr, &target); + return switch wasi.sock_send_to(s, &vec, 1, 0, &target, &out_len) { + case .Success => Result(i32, io.Error).{ Ok = out_len }; + case .Again => Result(i32, io.Error).{ Err = .NoData }; + case #default => Result(i32, io.Error).{ Err = .EOF }; + }; +} + +__net_sock_recv :: (s: SocketData, buf: [] u8) -> Result(i32, io.Error) { + vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count }; + + out_len: u32; + out_flags: wasi.ROFlags; + return switch wasi.sock_recv(s, &vec, 1, 0, &out_len, &out_flags) { + case .Success => Result(i32, io.Error).{ Ok = out_len }; + case .Again => Result(i32, io.Error).{ Err = .NoData }; + case #default => Result(i32, io.Error).{ Err = .EOF }; + }; +} + +__net_sock_send :: (s: SocketData, buf: [] u8) -> Result(i32, io.Error) { + vec := wasi.IOVec.{ buf = cast(i32) buf.data, len = buf.count }; + + out_len: u32; + return switch wasi.sock_send(s, &vec, 1, 0, &out_len) { + case .Success => Result(i32, io.Error).{ Ok = out_len }; + case .Again => Result(i32, io.Error).{ Err = .NoData }; + case #default => Result(i32, io.Error).{ Err = .EOF }; + }; +} + +__net_sock_shutdown :: (s: SocketData, how: SocketShutdown) -> io.Error { + sd := switch how { + case .Read => wasi.SDFlags.RD; + case .Write => wasi.SDFlags.WR; + case .ReadWrite => wasi.SDFlags.RD | wasi.SDFlags.WR; + }; + + return switch wasi.sock_shutdown(s, sd) { + case .Success => .None; + case #default => .OperationFailed; + }; +} + +__net_sock_close :: (s: SocketData) -> void { + wasi.fd_close(s); +} + +__net_resolve :: (host: str, port: u16, out_addrs: [] SocketAddress) -> i32 { +} + + +#package { + socket_addr_to_wasix_addr :: (in: &SocketAddress, out: &wasi.AddrPort) { + switch *in { + case &path: .Unix { + *out = .{ Unix = .{ *cast(&[108] u8) path } }; + } + + case &inet: .Inet { + *out = .{ Ipv4 = .{ inet.port, *cast(&wasi.AddrIPv4)&inet.addr } }; + } + + case &inet: .Inet6 { + // Not entirely right... + *out = .{ Ipv6 = .{ inet.port, *cast(&wasi.AddrIPv6)&inet.addr } }; + } + + case .HostPort --- + } + } + + wasi_addr_to_socket_address :: (in: &wasi.AddrPort, out: &SocketAddress) { + switch *in { + case .Unspec --- + + case &inet: .Ipv4 { + *out = .{ Inet = .{ inet.port, *cast(&u32)&inet.addr } }; + } + + case &inet: .Ipv6 { + *out = .{ Inet6 = .{ inet.port, *cast(&[16] u8)&inet.addr } }; + } + + case &unix: .Unix { + p: [256] u8 = *cast(&[256] u8) &unix.path; + *out = .{ Unix = p }; + } + } + } +} \ No newline at end of file