# Actor Model Pattern

## Problem

Shared mutable state accessed by multiple threads requires careful locking, which is error-prone (deadlocks, race conditions, priority inversion). As concurrency grows, lock contention becomes a bottleneck.

## Solution

Encapsulate state within **actors** -- independent units that communicate exclusively through asynchronous message passing. Each actor has a private mailbox (queue), processes messages sequentially, and never shares its state directly. This eliminates the need for locks.

## When to Use

- Highly concurrent systems with many independent entities (chat users, game entities, IoT devices)
- Distributed systems where components communicate over a network
- When you need fault isolation (one actor's failure doesn't corrupt another's state)
- Event-driven architectures with complex message routing

## When to Avoid

- Simple request-response flows with minimal shared state
- When synchronous, tightly-coupled communication is required
- Low-latency paths where message queuing overhead is unacceptable
- When debugging message flows is harder than debugging shared state

## Pseudocode

```
actor CounterActor:
    state count = 0
    mailbox = Queue()

    on_message("increment", amount):
        count += amount
        print("Count is now: " + count)

    on_message("get"):
        reply(count)

// Usage
counter = spawn(CounterActor)
send(counter, "increment", 5)
send(counter, "increment", 3)
result = send_and_wait(counter, "get")
print("Final count: " + result)
```

## Python

```python
import threading
import queue
from typing import Any

class Actor:
    def __init__(self, name: str):
        self.name = name
        self.mailbox: queue.Queue = queue.Queue()
        self._running = True
        self._thread = threading.Thread(target=self._run, daemon=True)
        self._thread.start()

    def _run(self) -> None:
        while self._running:
            try:
                message = self.mailbox.get(timeout=0.1)
                self.on_message(message)
            except queue.Empty:
                continue

    def on_message(self, message: Any) -> None:
        raise NotImplementedError

    def send(self, message: Any) -> None:
        self.mailbox.put(message)

    def stop(self) -> None:
        self._running = False
        self._thread.join()

class CounterActor(Actor):
    def __init__(self, name: str):
        self.count = 0
        super().__init__(name)

    def on_message(self, message: dict) -> None:
        action = message.get("action")
        if action == "increment":
            self.count += message["amount"]
            print(f"[{self.name}] Count incremented to {self.count}")
        elif action == "get":
            callback = message["callback"]
            callback(self.count)
        elif action == "stop":
            self._running = False

class PrinterActor(Actor):
    def on_message(self, message: str) -> None:
        print(f"[{self.name}] Received: {message}")

def main() -> None:
    counter = CounterActor("Counter")
    printer = PrinterActor("Printer")

    # Send messages
    counter.send({"action": "increment", "amount": 5})
    counter.send({"action": "increment", "amount": 3})
    counter.send({"action": "increment", "amount": 2})

    # Get result via callback
    result_event = threading.Event()
    result_holder: list = []

    def on_result(value: int) -> None:
        result_holder.append(value)
        printer.send(f"Counter value is {value}")
        result_event.set()

    counter.send({"action": "get", "callback": on_result})
    result_event.wait(timeout=2.0)

    print(f"Final count: {result_holder[0]}")

    counter.stop()
    printer.stop()

if __name__ == "__main__":
    main()
```

## Go

```go
package main

import (
	"fmt"
	"sync"
)

// Message types
type IncrementMsg struct {
	Amount int
}

type GetMsg struct {
	Reply chan int
}

type StopMsg struct{}

// Actor receives messages on a channel
type CounterActor struct {
	mailbox chan interface{}
	count   int
	wg      sync.WaitGroup
}

func NewCounterActor() *CounterActor {
	a := &CounterActor{
		mailbox: make(chan interface{}, 100),
	}
	a.wg.Add(1)
	go a.run()
	return a
}

func (a *CounterActor) run() {
	defer a.wg.Done()
	for msg := range a.mailbox {
		switch m := msg.(type) {
		case IncrementMsg:
			a.count += m.Amount
			fmt.Printf("[Counter] Count incremented to %d\n", a.count)
		case GetMsg:
			m.Reply <- a.count
		case StopMsg:
			return
		}
	}
}

func (a *CounterActor) Send(msg interface{}) {
	a.mailbox <- msg
}

func (a *CounterActor) Stop() {
	a.Send(StopMsg{})
	a.wg.Wait()
	close(a.mailbox)
}

func main() {
	counter := NewCounterActor()

	counter.Send(IncrementMsg{Amount: 5})
	counter.Send(IncrementMsg{Amount: 3})
	counter.Send(IncrementMsg{Amount: 2})

	// Get the value
	reply := make(chan int, 1)
	counter.Send(GetMsg{Reply: reply})
	result := <-reply

	fmt.Printf("Final count: %d\n", result)

	counter.Stop()
}
```

## JavaScript

```javascript
class Actor {
  constructor(name) {
    this.name = name;
    this.mailbox = [];
    this.processing = false;
  }

  send(message) {
    this.mailbox.push(message);
    if (!this.processing) {
      this._processNext();
    }
  }

  async _processNext() {
    if (this.mailbox.length === 0) {
      this.processing = false;
      return;
    }
    this.processing = true;
    const message = this.mailbox.shift();
    await this.onMessage(message);
    // Use microtask to process next message
    queueMicrotask(() => this._processNext());
  }

  async onMessage(message) {
    throw new Error("Must implement onMessage");
  }
}

class CounterActor extends Actor {
  constructor(name) {
    super(name);
    this.count = 0;
  }

  async onMessage(message) {
    switch (message.action) {
      case "increment":
        this.count += message.amount;
        console.log(`[${this.name}] Count incremented to ${this.count}`);
        break;
      case "get":
        message.callback(this.count);
        break;
    }
  }
}

class PrinterActor extends Actor {
  async onMessage(message) {
    console.log(`[${this.name}] Received: ${message}`);
  }
}

async function main() {
  const counter = new CounterActor("Counter");
  const printer = new PrinterActor("Printer");

  counter.send({ action: "increment", amount: 5 });
  counter.send({ action: "increment", amount: 3 });
  counter.send({ action: "increment", amount: 2 });

  const result = await new Promise((resolve) => {
    counter.send({ action: "get", callback: resolve });
  });

  printer.send(`Counter value is ${result}`);
  console.log(`Final count: ${result}`);

  // Let microtasks flush
  await new Promise((r) => setTimeout(r, 10));
}

main();
```

## Related Patterns

- **Message Queue** — an actor's mailbox is a message queue; actor model extends this with per-actor state and supervision.
- **Mediator** — an actor supervisor acts as a mediator coordinating child actor lifecycle.
- **Immutability** — messages passed between actors should be immutable to avoid shared mutable state.
