# dista.socket

> A module of Dista's API.

IPv4 TCP and process control over libc.

Just enough of `socket(2)` and friends for dista's processes: listeners on
loopback or on every interface, streams to a host name or to loopback, the
non-blocking reads and writes the head's event loop uses, `poll`, and
`fork`/`waitpid`/`_exit` to start and reap local processes. macOS and Linux
differ in `sockaddr_in`'s first byte, in `addrinfo`'s field order, in the
`SOL_SOCKET`, `MSG_DONTWAIT` and `EAGAIN` constants, and in how a write to a
closed peer is kept from raising SIGPIPE; `platform_map` picks the right value
for each.

## Structs

### `Stream`

```mojo
struct Stream
```

A connected TCP socket. It's non-blocking, but `write_all` and `read_into` wait for it, so to its users it blocks; the head's loop uses `send_some` and `recv_some`, which don't. Closes itself when dropped.

Implements: `Movable`

Fields:

- `fd: c_int`

#### `__init__`

```mojo
def __init__(out self, fd: Int32)
```

#### `__deinit__`

```mojo
def __deinit__(deinit self)
```

#### `connect`

```mojo
def connect(port: Int) -> Self
```

Connects to 127.0.0.1:`port`.

```mojo
def connect(host: String, port: Int) -> Self
```

Connects to `host`:`port`; `host` is a name or a dotted quad.

```mojo
def connect(ip: Array[UInt8, Int(4)], port: Int) -> Self
```

Connects to `ip`:`port`.

#### `local_ip`

```mojo
def local_ip(self) -> Array[UInt8, Int(4)]
```

The local IPv4 address this connection goes out from.

#### `write_all`

```mojo
def write_all(self, data: Span[UInt8])
```

Writes every byte of `data`.

#### `read_into`

```mojo
def read_into(self, buf: Pointer[UInt8], size: Int) -> Int
```

Reads exactly `size` bytes into `buf`.

Returns `size`, or 0 when the peer closed before sending anything.
Raises if the peer closes partway through.

#### `close`

```mojo
def close(mut self)
```

### `Listener`

```mojo
struct Listener
```

A TCP listener. Closes itself when dropped.

Implements: `Movable`

Fields:

- `fd: c_int`
- `port: Int`

#### `__init__`

```mojo
def __init__(out self, port: Int = Int(0), any_interface: Bool = False, backlog: Int = Int(128))
```

Binds and starts listening.

Args:

- `port`: The port, or 0 for one the OS picks.
- `any_interface`: Listen on every interface instead of loopback.
- `backlog`: How many connections may wait to be accepted.

#### `__deinit__`

```mojo
def __deinit__(deinit self)
```

#### `accept`

```mojo
def accept(self) -> Stream
```

Waits for the next connection.

#### `close`

```mojo
def close(mut self)
```

## Functions

### `resolve_ipv4`

```mojo
def resolve_ipv4(host: String) -> Array[UInt8, Int(4)]
```

The first IPv4 address of `host`, a name or a dotted quad.

### `send_some`

```mojo
def send_some(fd: Int32, data: Pointer[UInt8], size: Int) -> Int
```

Sends what the socket takes without blocking. Returns the bytes sent.

### `recv_some`

```mojo
def recv_some(fd: Int32, buf: Pointer[UInt8], size: Int) -> Int
```

Reads what has arrived, without blocking.

Returns the bytes read, 0 at end of stream, or -1 if nothing is waiting.

### `fcntl`

```mojo
def fcntl(fd: Int32, cmd: Int32, arg: Int32) -> c_int
```

`fcntl(2)` with one int argument: the only declaration of it in dista, since two with different signatures don't link.

### `set_nonblocking`

```mojo
def set_nonblocking(fd: Int32) -> Bool
```

Makes `fd` non-blocking; whether that worked.

### `wall_ns`

```mojo
def wall_ns() -> UInt64
```

Nanoseconds since the epoch, from CLOCK_REALTIME.

The timeline stamps tasks on the head and on workers with this, so the
stamps compare across processes and, NTP permitting, machines; a
monotonic clock only compares within one host.

### `stream_pair`

```mojo
def stream_pair() -> List[Stream]
```

Two connected local stream sockets, for tests and fuzzing.

### `poll`

```mojo
def poll(fds: List[Int32], events: List[Int16], timeout_ms: Int = Int(-1)) -> List[Int16]
```

Waits for any of `events[i]` on `fds[i]`, or until `timeout_ms` passes.

Returns each descriptor's `revents`. A timeout of -1 waits indefinitely.

### `poll_readable`

```mojo
def poll_readable(fds: List[Int32], timeout_ms: Int = Int(-1)) -> List[Bool]
```

Waits until at least one of `fds` is readable, or `timeout_ms` passes.

A closed or failed socket counts as readable, so the read that follows
reports it. A timeout of -1 waits indefinitely.

### `fork`

```mojo
def fork() -> Int
```

Forks the process. Returns 0 in the child, the child's pid in the parent.

### `wait_child`

```mojo
def wait_child(pid: Int) -> Int
```

Waits for child `pid` to exit and returns its raw wait status.

### `exit_now`

```mojo
def exit_now(code: Int)
```

Ends the process immediately, without running atexit handlers.

A forked worker leaves through here, so it never returns into the driver's
`main` or flushes the driver's buffered output a second time.

### `addr_key`

```mojo
def addr_key(ip: Array[UInt8, Int(4)], port: Int) -> String
```

## Aliases

### `AF_INET`

```mojo
comptime AF_INET = Int32(2)
```

### `SOCK_STREAM`

```mojo
comptime SOCK_STREAM = Int32(1)
```

### `SOL_SOCKET`

```mojo
comptime SOL_SOCKET = SIMD(platform_map[Optional(String("SOL_SOCKET")), linux=Optional(Int(1)), macos=Optional(Int(65535))]())
```

### `SO_REUSEADDR`

```mojo
comptime SO_REUSEADDR = SIMD(platform_map[Optional(String("SO_REUSEADDR")), linux=Optional(Int(2)), macos=Optional(Int(4))]())
```

### `SO_NOSIGPIPE`

```mojo
comptime SO_NOSIGPIPE = SIMD(platform_map[Optional(String("SO_NOSIGPIPE")), linux=Optional(Int(0)), macos=Optional(Int(4130))]())
```

### `MSG_NOSIGNAL`

```mojo
comptime MSG_NOSIGNAL = SIMD(platform_map[Optional(String("MSG_NOSIGNAL")), linux=Optional(Int(16384)), macos=Optional(Int(0))]())
```

### `EINTR`

```mojo
comptime EINTR = Int32(4)
```

### `IPPROTO_TCP`

```mojo
comptime IPPROTO_TCP = Int32(6)
```

### `TCP_NODELAY`

```mojo
comptime TCP_NODELAY = Int32(1)
```

### `SOCKADDR_IN_SIZE`

```mojo
comptime SOCKADDR_IN_SIZE = 16
```

### `EAGAIN`

```mojo
comptime EAGAIN = SIMD(platform_map[Optional(String("EAGAIN")), linux=Optional(Int(11)), macos=Optional(Int(35))]())
```

### `F_GETFL`

```mojo
comptime F_GETFL = Int32(3)
```

### `F_SETFL`

```mojo
comptime F_SETFL = Int32(4)
```

### `O_NONBLOCK`

```mojo
comptime O_NONBLOCK = SIMD(platform_map[Optional(String("O_NONBLOCK")), linux=Optional(Int(2048)), macos=Optional(Int(4))]())
```

### `MSG_DONTWAIT`

```mojo
comptime MSG_DONTWAIT = SIMD(platform_map[Optional(String("MSG_DONTWAIT")), linux=Optional(Int(64)), macos=Optional(Int(128))]())
```

### `ADDRINFO_SIZE`

```mojo
comptime ADDRINFO_SIZE = 48
```

### `ADDRINFO_FAMILY_OFF`

```mojo
comptime ADDRINFO_FAMILY_OFF = 4
```

### `ADDRINFO_SOCKTYPE_OFF`

```mojo
comptime ADDRINFO_SOCKTYPE_OFF = 8
```

### `ADDRINFO_ADDR_OFF`

```mojo
comptime ADDRINFO_ADDR_OFF = platform_map[Optional(String("AI_ADDR_OFF")), linux=Optional(Int(24)), macos=Optional(Int(32))]()
```

### `POLLIN`

```mojo
comptime POLLIN = Int16(1)
```

### `POLLOUT`

```mojo
comptime POLLOUT = Int16(4)
```

### `POLLERR`

```mojo
comptime POLLERR = Int16(8)
```

### `POLLHUP`

```mojo
comptime POLLHUP = Int16(16)
```

### `POLLFD_SIZE`

```mojo
comptime POLLFD_SIZE = 8
```
