Skip to content

Commit

Permalink
Add IO documentation to "Getting started"
Browse files Browse the repository at this point in the history
This replaces the documentation from the internals section with
something a little more useful.

This closes #535.

Changelog: other
  • Loading branch information
yorickpeterse committed Jun 13, 2023
1 parent 2d143ff commit 269e77d
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 38 deletions.
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ nav:
- getting-started/error-handling.md
- getting-started/pattern-matching.md
- getting-started/concurrency.md
- getting-started/io.md
- getting-started/modules.md
- Reference:
- guides/syntax.md
Expand Down
94 changes: 94 additions & 0 deletions docs/source/getting-started/io.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Asynchronous IO

IO operations, such as writing to a file or reading from a socket, are
asynchronous in Inko. Unlike other languages, there is no
[async/await](https://en.wikipedia.org/wiki/Async/await) and no [function
colouring](http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/).
Inko achieves this by baking asynchronous IO into the language, much like
languages such as [Erlang](https://www.erlang.org/) and [Go](https://go.dev/).

In plain English: Inko's runtime ensures an IO operation can't prevent other
processes from running.

## Sockets

Sockets are implemented as non-blocking sockets. When performing an operation
that would block, the process is suspended. A background thread called the
"network poller" then keeps an eye on the process, rescheduling it when the
operation is ready. The network poller uses epoll on Linux, and kqueue on macOS
and the various BSDs.

By default a single network poller thread is used, but the amount is
configurable using the
[`INKO_NETPOLL_THREADS`](../../guides/scaling/#environment-variables)
environment variable.

Sockets are provided by the module `std::net::socket`. The following socket
types exist in this module:

- `Socket`: a low-level IPv4/IPv6 socket. You probably don't want to use this
directly unless necessary.
- `UdpSocket`: a UDP IPv4/IPv6 socket.
- `TcpClient`: an IPv4/IPv6 TCP stream socket acting as a client.
- `TcpServer`: an IPv4/IPv6 TCP stream socket acting as a server.
- `UnixSocket`: a low-level Unix domain socket.
- `UnixDatagram`: a Unix datagram socket.
- `UnixClient`: a Unix stream socket acting as a client.
- `UnixServer`: a Unix stream socket acting as a server.

## Files and standard input/output

Other IO operations that don't support non-blocking operations, such as reading
from a file or writing to STDERR, use a different approach to ensure they don't
block the OS thread.

When such an operation is performed, Inko's runtime keeps track of how long the
operation is running for. If this takes too long, a backup OS thread is woken up
and takes over the work of the OS thread performing the blocking operation. When
the blocked thread wakes up again it reschedules the process, then turns itself
into a backup thread.

The amount of backup threads used is configured using the
[`INKO_BACKUP_THREADS`](../../guides/scaling/#environment-variables) environment
variable.

### Files

Types for working with files are provided in the module `std::fs::file`. The
following types are provided:

- `ReadOnlyFile`: opens a file that only allows reads
- `WriteOnlyFile`: opens a file that only allows writes
- `ReadWriteFile`: opens a file that allows both reads and writes

Instances of these types are created using the static `new` method, for example:

```inko
import std::fs::file::WriteOnlyFile
WriteOnlyFile.new('test.txt').expect('failed to open the file')
```

`WriteOnlyFile` and `ReadWriteFile` place the file cursor at the start of the
file, overwriting existing content when writing. To instead append to the end of
the file, use the `append` static method:

```inko
import std::fs::file::WriteOnlyFile
WriteOnlyFile.append('test.txt').expect('failed to open the file')
```

### Standard input/output

The module `std::stdio` provides types for working with standard input/output
streams. These types are as follows:

- `STDIN`: a type for reading from the standard input stream.
- `STDOUT`: a type for writing to the standard output stream.
- `STDERR`: a type for writing to the standard error stream.

## Other IO types

The module `std::io` provides various traits implemented by other IO types. For
example, the `Read` trait is implemented by IO types that support reads.
38 changes: 0 additions & 38 deletions docs/source/internals/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,44 +44,6 @@ The default reduction count is 1000 and can be changed by setting the
environment variable `INKO_REDUCTIONS` to a value between 1 and 65 535. The
higher the value, the more time a process is allowed to run for.

## IO operations

### Sockets

For network IO the runtime uses non-blocking sockets. When performing an
operation that would block, the process and its socket are registered with "the
network poller". This is a system/thread that polls a list of sockets until they
are ready, rescheduling their corresponding processes. Polling is done using
APIs such as epoll on Linux, kqueue on macOS/BSD, and IO completion ports on
Windows.

By default a single network poller thread is spawned, and each process thread
uses the same poller. The number of poller threads is configured using the
`INKO_NETPOLL_THREADS` environment variable. This variable can be set to a value
between 1 and 127. When the value is greater than one, network poller threads
are assigned to process threads in a round-robin fashion. Most programs won't
need more than a single thread, but if you make heavy use of (many) sockets you
may want to increase this value.

### Blocking IO

For blocking operations, such as file IO, Inko uses a fixed amount of backup
threads. When an OS thread is about to enter a blocking operation, it sets a
flag indicating when it did so. This is implemented such that it in most cases
it won't take more than 100-200 nanoseconds.

In the background a monitor thread periodically examines all OS threads. If it
finds an OS thread is blocking for too long, it wakes up a backup thread to take
over the work of this blocking OS thread. When the blocking OS thread finishes
the blocking call it continues running its process. When the process is
rescheduled and the OS thread would pick up new work, it becomes a backup thread
instead.

The number of backup threads is controlled using the environment variable
`INKO_BACKUP_THREADS` and defaults to four times the number of CPU cores. The
monitor thread runs at an interval of 100 microseconds, though the exact
interval may differ between platforms. This interval can't be changed.

## Timeouts

Processes can suspend themselves with a timeout, or await a future for up to a
Expand Down

0 comments on commit 269e77d

Please sign in to comment.