# B6 — Le compteur partagé

## Avant de commencer

Ce carnet facultatif prolonge le compteur et les clients concurrents d’AP7. Il ne constitue pas un devoir supplémentaire. Si je le retiens comme bonus pour un cycle, je préciserai en cours les exercices attendus, le dossier de dépôt et l’échéance. Les anciennes dates de remise ne s’appliquent plus.

## Le sujet

Dans un chat ou un serveur web, plusieurs connexions vivent en même temps. Nous allons isoler leur état partagé dans un GenServer plutôt que le recopier dans chaque connexion.

Écrivez `Counter.start_link/0`, `Counter.increment/1`, `Counter.value/1` et `Counter.reset/1`. L'incrément est un appel synchrone qui renvoie la **nouvelle** valeur. Le compteur commence à zéro, la remise à zéro renvoie `:ok`. Chaque compteur a son propre PID ; aucun nom global n'est nécessaire.

Lancez cent tâches qui incrémentent le même compteur. Vérifiez que les valeurs retournées couvrent exactement `1..100`, même si les tâches terminent dans un autre ordre. Puis lancez un second compteur et vérifiez leur indépendance.

Pour aller plus loin, branchez-le sur le serveur HTTP de la séance : seul `/` compte comme visite, `/favicon.ico` ne compte pas. Arrêtez vos processus après l'expérience.

## À vous

```elixir
defmodule Counter do
  use GenServer
  def start_link, do: GenServer.start_link(__MODULE__, 0)
  def increment(pid), do: GenServer.call(pid, :increment)
  def value(pid), do: GenServer.call(pid, :value)
  def reset(pid), do: GenServer.call(pid, :reset)
  @impl true
  def init(value), do: {:ok, value}
  @impl true
  def handle_call(:increment, _from, value), do: {:reply, nil, value}
  def handle_call(:value, _from, value), do: {:reply, nil, value}
  def handle_call(:reset, _from, value), do: {:reply, nil, value}
end
```

## Vérifications

```elixir
{:ok, first} = Counter.start_link()
{:ok, second} = Counter.start_link()
try do
  0 = Counter.value(first)
  tasks = for _ <- 1..100, do: Task.async(fn -> Counter.increment(first) end)
  values = Enum.map(tasks, &Task.await/1)
  true = Enum.sort(values) == Enum.to_list(1..100)
  100 = Counter.value(first)
  0 = Counter.value(second)
  1 = Counter.increment(second)
  :ok = Counter.reset(first)
  0 = Counter.value(first)
  1 = Counter.value(second)
after
  GenServer.stop(first)
  GenServer.stop(second)
end
IO.puts("Cent incréments, aucun perdu")
```

## Votre explication

Ajoutez une cellule Markdown pour expliquer votre démarche, les cas limites vérifiés et ce que vous avez observé. Pour toute question : aurelien.noce@minesparis.psl.eu, avec aurelien.noce@imagine-app.fr en copie.
