Shared\Flag
OxPHP\Shared\Flag est un booléen atomique à l'échelle du processus — le jumeau booléen de Shared\Atomic. Chaque opération est sans verrou ; deux workers qui inversent le drapeau simultanément ne peuvent pas observer d'état intermédiaire.
Vue d'ensemble
- Booléen atomique. Un seul bit d'état, avec
load/store/swap/compareAndSet. - Ordonnancement mémoire explicite. Chaque opération accepte un
Orderingoptionnel, dont la valeur par défaut estSeqCst, exactement commeShared\Atomic. - Sans verrou. Toutes les mutations tiennent en une seule instruction atomique du CPU. Sûr en cas de contention.
- Partageable. Les instances vivent dans le registre et peuvent être stockées dans un
Shared\Map, transmises via des capturesuse, etc.
Référence de l'API
namespace OxPHP\Shared;
final class Flag implements Shareable
{
public function __construct(bool $initial = false);
public function load(Ordering $order = Ordering::SeqCst): bool; // Relaxed | Acquire | SeqCst
public function store(bool $value, Ordering $order = Ordering::SeqCst): void; // Relaxed | Release | SeqCst
public function swap(bool $value, Ordering $order = Ordering::SeqCst): bool; // any ordering; returns previous
public function compareAndSet(
bool $expect,
bool $new,
Ordering $success = Ordering::SeqCst,
Ordering $failure = Ordering::SeqCst, // Relaxed | Acquire | SeqCst
): bool;
public function id(): int;
}| Méthode | Retour | Cas d'usage |
|---|---|---|
load |
actuelle | Simple lecture. |
store |
void | Affecte une valeur explicite sans condition. |
swap |
précédente | Affecte une valeur explicite ; la valeur de retour indique si vous l'avez modifiée. swap(true) est un test-and-set (« ai-je gagné ? »). |
compareAndSet |
échangé ? | Initialisation one-shot : ne réussit que si le drapeau valait la valeur attendue. |
Exemples
Kill-switch
<?php
use OxPHP\Shared\Flag;
$maintenance = new Flag();
// In a request handler
if ($maintenance->load()) {
http_response_code(503);
header('Retry-After: 60');
echo 'under maintenance';
return;
}
// In an admin endpoint
$maintenance->store(true); // enable
$maintenance->store(false); // disableVainqueur de l'initialisation one-shot
<?php
use OxPHP\Shared\Flag;
$migrated = new Flag();
if ($migrated->compareAndSet(expect: false, new: true)) {
// First worker to arrive wins — run the migration once.
runSchemaMigration();
} else {
// Someone else already ran it.
}Déclenchement du circuit breaker
<?php
use OxPHP\Shared\Flag;
$tripped = new Flag();
try {
callDownstream();
} catch (DownstreamFailedException $e) {
$wasAlreadyTripped = $tripped->swap(true); // set true, learn the prior state
if (!$wasAlreadyTripped) {
alertOncall($e); // fire alert only on first trip
}
throw $e;
}Pour un circuit breaker complet, vous aurez généralement besoin d'un Shared\Counter pour la fenêtre d'échecs et d'un Shared\Flag pour l'état déclenché — réinitialisez le drapeau via store(false) une fois que la fenêtre est retombée au calme.
Publier une charge utile, puis signaler avec un ordonnancement moins coûteux
<?php
use OxPHP\Shared\Flag;
use OxPHP\Shared\Map;
use OxPHP\Shared\Ordering;
$ready = new Flag();
$config = new Map();
// Producer: write the payload, then publish with Release.
$config->set('dsn', $dsn);
$ready->store(true, Ordering::Release);
// Consumer: an Acquire load that observes `true` also observes the payload.
if ($ready->load(Ordering::Acquire)) {
$dsn = $config->get('dsn');
}Sémantique et pièges
swap renvoie la valeur précédente, qui est le retour le plus utile : « ai-je changé quelque chose ? » se résume à $prev !== $new, et swap(true) est le test-and-set canonique. store renvoie void ; si vous avez besoin de la valeur antérieure, utilisez swap.
compareAndSet est la façon d'exprimer « le premier arrivé l'emporte ». Un simple store(true) réussit toujours, il ne peut donc pas exprimer « ne pas écraser si la valeur est déjà définie ».
L'ordonnancement mémoire est identique à celui de Shared\Atomic. load rejette Release/AcqRel, store rejette Acquire/AcqRel, et le $failure de compareAndSet rejette Release/AcqRel — chacun lève une InvalidOrderingException. La valeur par défaut SeqCst est toujours sûre.
Un drapeau ne bloque pas. Si vous devez attendre une transition, associez-le à un Shared\Channel ou utilisez Shared\Once.
Exceptions
| Exception | Levée par |
|---|---|
StaleHandleException |
Toute méthode sur un handle dont l'entrée de registre a été évincée. |
UninitializedException |
id() sur un wrapper dont le __construct n'est pas terminé. |
InvalidOrderingException |
Un Ordering non autorisé pour l'opération (voir ci-dessus). |
Observabilité
Voir Observabilité de l'état partagé. Références rapides :
GET /__ox_shared/entry?id=Nexpose{ value: true|false, type: "Flag" }.- Jauge Prometheus
oxphp_shared_flag_value{flag_id="…"}(0 ou 1). - Les métriques à l'échelle du registre couvrent Flag via le label
type="Flag".
Quand ne pas l'utiliser
- Logique à plusieurs états. Un drapeau n'a que deux valeurs. S'il vous faut idle/busy/done ou toute machine à trois états, tournez-vous vers
Shared\Counter(avec des valeurs d'enum entières) ouShared\Mutexsur un tableau façon enum. - Attendre une transition. Les drapeaux ne bloquent pas. Associez-le à un
Shared\Channel(ou à unShared\Counterque vous interrogez parcompareAndSet) lorsqu'un worker doit attendre que le drapeau s'inverse. - Compter des événements. Un drapeau n'est pas un compteur. Utilisez
Shared\Counterpour les décomptes. - État entier. Si le commutateur est en réalité un petit entier, utilisez directement
Shared\Atomic.
Voir aussi
- État partagé — vue d'ensemble et modèle mental.
- Shared\Atomic — le jumeau int64, même modèle d'ordonnancement.
- Shared\Counter — quand vous avez besoin de plus que on/off.
- Shared\Once — quand la valeur calculée une seule fois est plus riche qu'un booléen.
- Shared\Mutex — quand l'inversion d'un drapeau doit être validée conjointement avec un autre état.