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() :
$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.
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/storeetCounter::setci-dessus). - Si le type contient une collection de valeurs, il implémente
\Countableet exposecount(): int. - Les méthodes de lecture sont
getouload(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*Timeoutprendint $ms > 0et rejette les entrées nulles, négatives ou non entières avecTypeException. Les méthodes de politique d'attentetry*renvoient soit un Result typé par valeur, soit lèvent une exception de domaine — jamais unnull-encodé.Pool::tryAcquire(), qui renvoienulllorsque le pool est saturé, fait exception à cette règle. Les opérations à succès conditionnel suivent le nommage dédiésetIfAbsentplutôt quetry*. - 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().
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.