From 1ecab2fcbc4a52b481f185ccf87bb38c9691c39e Mon Sep 17 00:00:00 2001 From: Sokus Date: Wed, 8 Mar 2023 13:17:59 +0100 Subject: [PATCH] Add `set_blocking` for network sockets --- core/net/common.odin | 1 + core/net/errors_darwin.odin | 6 ++++++ core/net/errors_linux.odin | 8 ++++++++ core/net/errors_windows.odin | 12 ++++++++++++ core/net/socket.odin | 4 ++++ core/net/socket_darwin.odin | 6 ++++++ core/net/socket_linux.odin | 23 +++++++++++++++++++++++ core/net/socket_windows.odin | 12 ++++++++++++ core/os/os_linux.odin | 13 +++++++++++++ core/sys/unix/syscalls_linux.odin | 4 ++++ 10 files changed, 89 insertions(+) diff --git a/core/net/common.odin b/core/net/common.odin index a9f2ba9bb..70a027138 100644 --- a/core/net/common.odin +++ b/core/net/common.odin @@ -64,6 +64,7 @@ Network_Error :: union #shared_nil { UDP_Recv_Error, Shutdown_Error, Socket_Option_Error, + Set_Blocking_Error, Parse_Endpoint_Error, Resolve_Error, DNS_Error, diff --git a/core/net/errors_darwin.odin b/core/net/errors_darwin.odin index 645bbe555..39cf4c665 100644 --- a/core/net/errors_darwin.odin +++ b/core/net/errors_darwin.odin @@ -197,4 +197,10 @@ Socket_Option_Error :: enum c.int { Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), Reset_When_Keepalive_Set = c.int(os.ENOTCONN), Not_Socket = c.int(os.ENOTSOCK), +} + +Set_Blocking_Error :: enum c.int { + None = 0, + + // TODO: Add errors for `set_blocking` } \ No newline at end of file diff --git a/core/net/errors_linux.odin b/core/net/errors_linux.odin index 9575d7c18..9366f4435 100644 --- a/core/net/errors_linux.odin +++ b/core/net/errors_linux.odin @@ -190,4 +190,12 @@ Socket_Option_Error :: enum c.int { Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT), Reset_When_Keepalive_Set = c.int(os.ENOTCONN), Not_Socket = c.int(os.ENOTSOCK), +} + +Set_Blocking_Error :: enum c.int { + None = 0, + + // TODO: add errors occuring on followig calls: + // flags, _ := os.fcntl(sd, os.F_GETFL, 0) + // os.fcntl(sd, os.F_SETFL, flags | int(os.O_NONBLOCK)) } \ No newline at end of file diff --git a/core/net/errors_windows.odin b/core/net/errors_windows.odin index 154f6c2d8..272aa0a96 100644 --- a/core/net/errors_windows.odin +++ b/core/net/errors_windows.odin @@ -259,3 +259,15 @@ Socket_Option_Error :: enum c.int { Reset_When_Keepalive_Set = win.WSAENOTCONN, Not_Socket = win.WSAENOTSOCK, } + +Set_Blocking_Error :: enum c.int { + None = 0, + + Network_Subsystem_Failure = win.WSAENETDOWN, + Blocking_Call_In_Progress = win.WSAEINPROGRESS, + Not_Socket = win.WSAENOTSOCK, + + // TODO: are those errors possible? + Network_Subsystem_Not_Initialized = win.WSAENOTINITIALISED, + Invalid_Argument_Pointer = win.WSAEFAULT, +} \ No newline at end of file diff --git a/core/net/socket.odin b/core/net/socket.odin index 95eb50496..a24ae8a0b 100644 --- a/core/net/socket.odin +++ b/core/net/socket.odin @@ -173,4 +173,8 @@ shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_E set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error { return _set_option(socket, option, value, loc) +} + +set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) { + return _set_blocking(socket, should_block) } \ No newline at end of file diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin index a0a5af6ca..0d8503cfd 100644 --- a/core/net/socket_darwin.odin +++ b/core/net/socket_darwin.odin @@ -301,6 +301,12 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca return nil } +@(private) +_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) { + // TODO: Implement + unimplemented() +} + @private _endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH) { switch a in ep.address { diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin index 81419f6c9..690e09ab7 100644 --- a/core/net/socket_linux.odin +++ b/core/net/socket_linux.odin @@ -316,6 +316,29 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca return nil } +@(private) +_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) { + socket := any_socket_to_socket(socket) + + flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0) + if getfl_err != os.ERROR_NONE { + return Set_Blocking_Error(getfl_err) + } + + if should_block { + flags &= ~int(os.O_NONBLOCK) + } else { + flags |= int(os.O_NONBLOCK) + } + + _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags) + if setfl_err != os.ERROR_NONE { + return Set_Blocking_Error(setfl_err) + } + + return nil +} + @(private) _endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH) { switch a in ep.address { diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin index d7264d617..3b9623749 100644 --- a/core/net/socket_windows.odin +++ b/core/net/socket_windows.odin @@ -310,6 +310,18 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca return nil } +@(private) +_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) { + socket := any_socket_to_socket(socket) + arg: win.DWORD = 0 if should_block else 1 + res := win.ioctlsocket(win.SOCKET(socket), transmute(win.c_long)win.FIONBIO, &arg) + if res == win.SOCKET_ERROR { + return Set_Blocking_Error(win.WSAGetLastError()) + } + + return nil +} + @(private) _endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: win.SOCKADDR_STORAGE_LH) { switch a in ep.address { diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 85abb0d21..d001d5ec7 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -225,6 +225,11 @@ TCP_CORK: int : 3 MSG_TRUNC : int : 0x20 +// TODO: add remaining fcntl commands +// reference: https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h +F_GETFL: int : 3 /* Get file flags */ +F_SETFL: int : 4 /* Set file flags */ + // NOTE(zangent): These are OS specific! // Do not mix these up! RTLD_LAZY :: 0x001 @@ -1074,3 +1079,11 @@ shutdown :: proc(sd: Socket, how: int) -> (Errno) { } return ERROR_NONE } + +fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { + result := unix.sys_fcntl(fd, cmd, arg) + if result < 0 { + return 0, _get_errno(result) + } + return result, ERROR_NONE +} \ No newline at end of file diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index 779bc3be3..abdcf0b92 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -2053,6 +2053,10 @@ sys_personality :: proc(persona: u64) -> int { return int(intrinsics.syscall(SYS_personality, uintptr(persona))) } +sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int { + return int(intrinsics.syscall(SYS_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg))) +} + get_errno :: proc "contextless" (res: int) -> i32 { if res < 0 && res > -4096 { return i32(-res)