Conventions de nommage OxPHP\Shared\*

L'espace de noms OxPHP\Shared\* est l'API de concurrence au niveau applicatif : Atomic, Counter, Flag, Map, Channel, Mutex, Once, Pool. Les noms de méthodes suivent un ensemble unique de règles, ce qui permet aux utilisateurs de prédire l'API sans consulter la documentation de chaque type.

Ce document est la référence canonique. Les nouvelles primitives, ainsi que les modifications apportées aux primitives existantes, DOIVENT le respecter.

Règles

1. Lire une valeur — get()

Convention PHP. Utilisée par Map::get(), Counter::get(), Once::get().

Atomic::load(Ordering $order = Ordering::SeqCst) constitue l'exception délibérée : son existence porte l'argument d'ordonnancement, signalant que la lecture fait partie d'un contrat de modèle mémoire distinct d'un simple getter.

2. Écrire une valeur — set(), store() pour les atomiques

Map::set(), mise à jour de la valeur d'un Mutex (par référence dans withLock), Once::getOrInit(). Atomic::store(int $value, Ordering $order = Ordering::SeqCst) reflète load pour la même raison.

3. Nombre d'éléments — count(): int

Tout conteneur qui expose sa taille actuelle le fait sous le nom count(): int. Channel implémente en plus \Countable, si bien que count($ch) fonctionne comme un idiome natif pour les éléments en file d'attente. Map expose count(): int en tant que méthode mais n'implémente pas \Countable — appelez-la directement. Pool n'a pas de count() ; lisez ses emplacements vivants via stats()->size() :

php
$ch = new OxPHP\Shared\Channel(1024); $map = new OxPHP\Shared\Map(); $pool = new OxPHP\Shared\Pool($factory); count($ch); // queued items (Channel implements \Countable) $map->count(); // entries $pool->stats()->size(); // total live slots (in-use + idle)

Pas de size(), len() ni pending() — ils sont interdits sur la surface publique, quelle que soit la langue dont proviennent les automatismes de l'implémenteur.

4. Getter booléen — préfixe is*()

Channel::isClosed().

Pas de verbes nus (test, check) ni de noms spécifiques au domaine (closed). Le préfixe is marque une lecture pure d'une propriété booléenne.

Un type dont l'état est plus riche qu'un simple booléen l'expose via une méthode status() renvoyant une énumération plutôt qu'un getter is*() — le RecvResult::status() de Channel et Once::status(): Once\Status (Uninitialized/Pending/Ready/Poisoned) suivent ce principe. Optez pour status() lorsque la réponse comporte plus de deux cas.

Mutex n'expose pas isCorrupted() — la corruption est persistante, irrécupérable, et remontée via CorruptedMutexException lors de la prochaine acquisition. Il n'y a rien d'utile à faire avec la sonde, si ce n'est réacquérir et intercepter.

5. Trichotomie de la politique d'attente — try* / nu / *Timeout

Les primitives bloquantes (Channel, Mutex) expriment la politique d'attente à travers le nom de la méthode, et non via un argument ?float $timeout surchargé :

Suffixe Comportement Exemples
try* Non bloquant ; signale immédiatement la variante d'échec. Channel::trySend, Channel::tryRecv, Mutex::tryWithLock
(nom nu) Bloque indéfiniment (ou jusqu'à l'annulation de la Fiber de la requête). Channel::send, Channel::recv, Mutex::withLock
*Timeout Attente bornée. Prend un int $ms > 0 obligatoire. Channel::sendTimeout, Channel::recvTimeout, Mutex::withLockTimeout

La trichotomie fait sortir trois politiques ambiguës (null = indéfiniment, 0 = try, positif = borné) d'un seul paramètre pour les répartir dans trois méthodes aux noms auto-documentés.

Note

L'argument $ms des méthodes *Timeout est strictement positif. Les valeurs nulles, négatives, non entières ou absentes lèvent OxPHP\Shared\TypeException au niveau du pont.

Les opérations à succès conditionnel résident sur Map sous l'appellation setIfAbsent plutôt que try* : Map::setIfAbsent valide uniquement lorsque la clé était absente (en parallèle de HashMap::try_insert) et renvoie la valeur existante, ou null lorsque l'insertion a eu lieu. Le nom setIfAbsent est réservé à cette unique sémantique ; ne le réutilisez pas ailleurs.

La manière dont une méthode try* signale un échec dépend du type : Channel renvoie un Result typé par valeur, Mutex lève une ContentionException, Pool::tryAcquire() renvoie null lorsque le pool est saturé, et Once::trySet() renvoie false lorsque la cellule est déjà Ready ou Pending.

6. Compare-and-swap — compareAndSet()

Atomic::compareAndSet(), Flag::compareAndSet(). Renvoie toujours bool (l'échange a eu lieu, ou non).

7. Remplacer et renvoyer la valeur précédente — swap()

Atomic::swap() pour les entiers, Flag::swap() pour les booléens. Renvoie la valeur précédente.

8. RMW atomique renvoyant la valeur précédente — préfixe fetch*()

Atomic::fetchAdd(), fetchSub(), fetchAnd(), fetchOr(), fetchXor().

Le préfixe fetch encode le contrat de retour : la valeur avant l'opération. Cela contraste avec Counter::add(), qui renvoie la nouvelle valeur (compteur agrégé de style LongAdder).

Lors de l'ajout de nouvelles méthodes RMW, choisissez d'abord le contrat, puis le nom :

  • retour de la valeur précédente → fetchVerb(args)
  • retour de la nouvelle valeur → verb(args) nu

Ne les mélangez pas.

9. Réinitialiser à la valeur par défaut — clear()

Map::clear() — vide le conteneur ; renvoie le nombre d'entrées supprimées.

Counter n'a pas de clear() — set(0) constitue sa remise à zéro par fenêtre. Counter::set() est l'exception documentée qui renvoie la valeur précédente (et non void) : il s'agit de l'échange atomique, et set(0) lisant le total antérieur correspond à l'idiome sumThenReset de LongAdder. (Atomic nomme la même opération swap() ; Counter conserve set parce que set($n) se lit naturellement pour l'amorçage et le fenêtrage.)

10. Identité dans le registre — id(): int

Chaque instance Shared\* expose id(): int pour les logs et l'endpoint d'observabilité /__ox_shared/entry?id=<id>.

Aide-mémoire

Concept Nom canonique Exemples
Lire une valeur get() Map::get, Counter::get
Lire un atomique load($order) Atomic::load
Écrire une valeur set() Map::set
Écrire un atomique store($v, $order) Atomic::store
Nombre d'éléments count(): int Map::count, Channel::count
Propriété booléenne is*(): bool Channel::isClosed
Insertion conditionnelle setIfAbsent($k, $v) Map::setIfAbsent
Attente non bloquante try*() Channel::trySend, Mutex::tryWithLock
Attente indéfinie verbe nu Channel::send, Channel::recv, Mutex::withLock
Attente bornée *Timeout(int $ms) Channel::sendTimeout, Mutex::withLockTimeout
Compare-and-swap compareAndSet() Atomic::compareAndSet
Échange, renvoie la préc. swap() Atomic::swap, Flag::swap
RMW atomique, renvoie la préc. fetch*() Atomic::fetchAdd
RMW atomique, renvoie la nouvelle verbe nu Counter::add
Réinitialiser à la valeur par défaut clear() Map::clear
ID de registre id(): int chaque type Shared\*

Ajouter un nouveau type Shared\*

Lorsque vous proposez une nouvelle primitive, remplissez cette liste de contrôle avant de fusionner :

  • Chaque méthode correspond à une ligne de l'aide-mémoire, ou dispose d'un ADR expliquant l'exception (voir Atomic::load/store et Counter::set ci-dessus).
  • Si le type contient une collection de valeurs, il implémente \Countable et expose count(): int.
  • Les méthodes de lecture sont get ou load (atomiques uniquement).
  • Les getters booléens utilisent le préfixe is*.
  • Les variantes de politique d'attente suivent la trichotomie try* / nu / *Timeout(int $ms). La variante *Timeout prend int $ms > 0 et rejette les entrées nulles, négatives ou non entières avec TypeException. Les méthodes de politique d'attente try* renvoient soit un Result typé par valeur, soit lèvent une exception de domaine — jamais un null-encodé. Pool::tryAcquire(), qui renvoie null lorsque le pool est saturé, fait exception à cette règle. Les opérations à succès conditionnel suivent le nommage dédié setIfAbsent plutôt que try*.
  • Pas de len, size, pending, test, ni d'autres noms ad hoc.
  • Les verbes spécifiques au domaine (evict, drain, flush, etc.) n'apparaissent que lorsqu'aucune entrée canonique de l'aide-mémoire ne couvre le concept.

Noms d'observabilité

La surface destinée aux opérateurs — les noms de métriques Prometheus et le JSON à /__ox_shared/entry?id=<id> — constitue un contrat distinct de l'API PHP, car la renommer casse les tableaux de bord et les règles d'alerte. Le nombre d'éléments d'un Channel ou d'un Pool est oxphp_shared_channel_count ou oxphp_shared_pool_count sur /metrics, et count dans son JSON type_specific. Pour un Channel, c'est le nombre que renvoie Channel::count() ; pour un Pool, ce sont les emplacements vivants, le nombre que renvoie Pool::stats()->size().

Supprimés en 0.13.0

Les anciennes appellations — oxphp_shared_channel_pending, oxphp_shared_pool_size, ainsi que les clés JSON pending et size — ne sont plus émises. Les tableaux de bord et les règles d'alerte encore écrits pour elles ne trouvent aucune série, si bien qu'une alerte à seuil sur ces noms ne se déclenche jamais.

Stabilité

Ces règles font partie du contrat OxPHP\Shared\* 1.0. Après la sortie de la 1.0, les renommages sont des changements cassants et nécessitent un cycle de dépréciation. Avant la 1.0, les règles restent contraignantes — les nouvelles méthodes qui les enfreignent seront rejetées en revue.

Une erreur ? Signalez-la →