Add support for non-blocking network IO
Details
This adds support for performing non-blocking network operations, such as reading and writing to/from a socket. The runtime API exposed is similar to Erlang, allowing one to write code that uses non-blocking APIs without having to resort to using callbacks. For example, in a typicall callback based language you may write the following to read from a socket:
socket.create do (socket) {
socket.read do (data) {
}
}
In Inko, you instead would (more or less) write the following:
import std::net::socket::TcpStream
let socket = try! TcpStream.new(ip: '192.0.2.0', port: 80)
let message = try! socket.read_string(size: 4)
The VM then takes care of using the appropriate non-blocking operations, and will reschedule processes whenever necessary.
This functionality is exposed through the following runtime modules:
- std::net::ip: used for parsing IPv4 and IPv6 addresses.
- std::net::socket: used for TCP and UDP sockets.
- std::net::unix: used for Unix domain sockets.
The VM uses the system's native polling mechanism to determine when a file descriptor is available for a read or write. On Linux we use epoll, while using kqueue for the various BSDs and Mac OS. For Windows we use wepoll (https://github.com/piscisaureus/wepoll). Wepoll exposes an API that is compatible with the epoll API, but uses Windows IO completion ports under the hoods.
When a process attempts to perform a non-blocking operation, the process is registered (combined with the file descriptor to poll) in a global poller and suspended. When the file descriptor becomes available for a read or write, the corresponding process is rescheduled. The polling mechanism is set up in such a way that a process can not be rescheduled multiple times at once.
Corresponding issue
Fixes #112 (closed)
Checklist
-
Write a more detailed description -
Explain why we're using epoll/kqueue/etc directly instead of MIO -
Set up a crate for wepoll bindings -
Add kqueue support -
Add wepoll support -
Write a proper MR description -
Investigate why LocalAllocator
needs more memory on nightly Rust (perhaps due to hash brown?)- This is indeed because of hashbrown (https://github.com/rust-lang/hashbrown/issues/69). Workaround is to use FNV
-
Handle unused variables on Windows: https://ci.appveyor.com/project/YorickPeterse/inko/builds/24170241/job/mhyg6pqfi30olulm -
Implement socket instructions -
Creating a socket (UDP, TCP socket, TCP listener) -
Obtaining the peer and local address -
Setting and getting socket options -
accept()
for TCP listeners -
send_to()
for UDP sockets -
recv_from
for UDP sockets -
read()
-
write()
-
connect()
for UDP sockets
-
-
Write standard library -
std::net::socket
-
Socket
-
UdpSocket
-
TcpStream
-
TcpListener
-
SocketAddress
-
-
std::net::ip
-
IpAddress
-
Ipv4Address
-
Ipv6Address
-
parse()
-
-
std::net::unix
-
UnixSocket
-
UnixDatagram
-
UnixListener
-
SocketAddress
-
-
-
Added tests -
Added documentation -
Inko source code follows the Inko style guide