# `CDPEx.Pool`
[🔗](https://github.com/patrols/cdp_ex/blob/v0.9.0/lib/cdp_ex/pool.ex#L1)

A fixed-size pool of reusable `CDPEx.Browser` processes.

Launching Chrome is expensive — a cold start can take several seconds on a
constrained host, with a fresh profile each time. A pool keeps browsers warm
and hands them out for reuse, so a per-job fetch no longer pays a launch on
every call.

    {:ok, pool} = CDPEx.Pool.start_link(size: 2, launch_opts: [headless: true])

    CDPEx.Pool.with_page(pool, fn page ->
      {:ok, _} = CDPEx.Page.navigate(page, "https://example.com")
      CDPEx.Page.html(page)
    end)

Browsers are launched **lazily** up to `:size` and reused thereafter.
`checkout/2` blocks (up to `:checkout_timeout`) when every browser is busy. The
pool is resilient: a caller that crashes while holding a browser has it returned
automatically, and a browser that crashes is dropped and relaunched on demand —
so `:size` self-heals. Put the pool under your supervision tree; its
`terminate/2` stops every browser, reaping Chrome.

Browser launches are **asynchronous** — each cold start (a cold Chrome can take a
few seconds) runs in its own task, so the pool keeps serving checkouts, checkins,
and timeouts while browsers warm up, and a pool growing to `:size` under load
launches them concurrently. `:checkout_timeout` is honored even during warmup.

## Options

  * `:size` — maximum number of browsers (default `1`)
  * `:launch_opts` — options passed to each `CDPEx.Browser` (see `CDPEx.Chrome`); any
    `:owner` here is ignored — the pool owns its browsers and sets it itself
  * `:checkout_timeout` — ms to wait for a free browser (default `5_000`)
  * `:name` — registers the pool process

# `t`

```elixir
@type t() :: %CDPEx.Pool{
  available: term(),
  busy: term(),
  count: term(),
  launch_opts: term(),
  launching: term(),
  size: term(),
  start_fun: term(),
  task_sup: term(),
  waiting: term()
}
```

# `checkin`

```elixir
@spec checkin(GenServer.server(), pid()) :: :ok
```

Returns a browser borrowed with `checkout/2`.

# `checkout`

```elixir
@spec checkout(GenServer.server(), timeout()) :: {:ok, pid()} | {:error, term()}
```

Borrows a browser, blocking up to `timeout` ms when all are busy.

Returns `{:ok, browser}`, `{:error, :timeout}`, or `{:error, reason}` if a
browser had to be launched and failed — a launch error is reported to a waiting
caller and is generally worth retrying. A caller still waiting when the pool stops
gets `{:error, :noproc}`. **Always** `checkin/2` it when done — or use
`with_browser/3` / `with_page/3`, which do that for you.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts a pool. See the moduledoc for options.

# `stop`

```elixir
@spec stop(GenServer.server()) :: :ok
```

Stops the pool, stopping every browser (and reaping Chrome).

# `with_browser`

```elixir
@spec with_browser(GenServer.server(), (pid() -&gt; result), timeout()) ::
  result | {:error, term()}
when result: var
```

Runs `fun` with a checked-out browser, returning it afterwards (even if `fun`
raises). Returns `fun`'s value, or `{:error, reason}` if no browser was free.

# `with_page`

```elixir
@spec with_page(GenServer.server(), (CDPEx.Page.t() -&gt; result), keyword()) ::
  result | {:error, term()}
when result: var
```

Runs `fun` with a fresh page on a pooled browser, cleaning up the page and
returning the browser afterwards. The pooled counterpart of `CDPEx.with_page/3`
— it reuses a warm browser instead of launching one per call.

`opts` are forwarded to `CDPEx.with_page/3` (e.g. `:prevent_alerts`); pass
`:checkout_timeout` to bound the wait for a free browser. Returns `fun`'s value,
or `{:error, reason}`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
