# Channels and actors

> Communicate with typed channels and serial actor mailboxes without unsafe shared mutation.

Canonical: https://alice-lang.org/documentation/concurrency/channels/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

## Send values through a channel

A channel connects producers and consumers. `Channel(T)` carries values of `T`.

```alice
ch = Channel(Int32).new
task = spawn {
  5.times { |i| ch.send(i) }
  ch.close
}
total = 0
ch.each { |n| total += n }
task.wait
puts total
```

Expected output:

```text
10
```

[Edit and run in the Alice playground](https://playground.alice-lang.org/#code=u.Y2ggPSBDaGFubmVsKEludDMyKS5uZXcKdGFzayA9IHNwYXduIHsKICA1LnRpbWVzIHsgfGl8IGNoLnNlbmQoaSkgfQogIGNoLmNsb3NlCn0KdG90YWwgPSAwCmNoLmVhY2ggeyB8bnwgdG90YWwgKz0gbiB9CnRhc2sud2FpdApwdXRzIHRvdGFs)

The producer sends five values and closes the channel. `each` consumes until the channel is closed and drained. The accumulator belongs to the consuming context, so the producer never mutates it. Waiting on the task also observes producer completion.

## Capacity and waiting

`Channel(T).new` currently creates an unbounded channel. `Channel(T).new(capacity)` with a positive capacity creates a bounded channel. A full bounded channel makes a send wait; this is useful for limiting queued work. Do not assume the default is a zero-capacity rendezvous channel.

| Method                   | Behavior                                                |
| ------------------------ | ------------------------------------------------------- |
| `send(value)`            | Send synchronously; can block on a full bounded channel |
| `receive`                | Receive synchronously; waits for a value                |
| `try_receive(out value)` | Attempt a nonblocking receive                           |
| `send_async(value)`      | Send without parking a waiting thread                   |
| `receive_async`          | Asynchronously receive a value                          |
| `wait_async`             | Await readiness; false after closure and draining       |
| `close`                  | Complete the writer                                     |
| `each { \|value\| ... }` | Consume until closure and draining                      |

A receive after completion and draining fails; use the completion-aware iteration or readiness API when the end of the stream is expected. Ensure a producer closes the channel on its relevant exit paths so a consumer does not wait indefinitely.

## Async consumption

In an `async def`, await `send_async` or `receive_async` when a wait is possible. `wait_async` can drive a consumer loop. Multiple consumers can race for values, so design completion and consumption together; readiness is not exclusive ownership of a value.

## Actors

`Actor(T)` owns a typed mailbox and calls one handler serially for each received message. Idle actors use an async mailbox loop rather than holding one thread per actor.

```alice
seen = Atomic.new(0)
actor = Actor(Int32).new(->(message : Int32) { seen.add(message) })
actor.send(3)
actor.send(4)
actor.stop
puts seen.get
```

`stop` completes the mailbox and waits for queued messages to drain. `stop_async` returns the completion task for async callers. Send all required messages before stopping; sending after shutdown is not a reliable delivery path.

The handler's access to outside state is still checked. An actor serializes its own handling; it does not make an unrelated captured collection safe for concurrent mutation elsewhere.

## Continue reading

- Previous: [Tasks, spawn, and async](https://alice-lang.org/documentation/concurrency/tasks.md)
- Next: [Mutexes and atomics](https://alice-lang.org/documentation/concurrency/synchronization.md)
