Browse Source

Add `set_blocking` for network sockets

Sokus 2 years ago
parent
commit
1ecab2fcbc

+ 1 - 0
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,

+ 6 - 0
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`
 }

+ 8 - 0
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))
 }

+ 12 - 0
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,
+}

+ 4 - 0
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)
 }

+ 6 - 0
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 {

+ 23 - 0
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 {

+ 12 - 0
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 {

+ 13 - 0
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
+}

+ 4 - 0
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)