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 Ordering optionnel, dont la valeur par défaut est SeqCst, exactement comme Shared\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 captures use, etc.

Référence de l'API

php
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
<?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); // disable

Vainqueur de l'initialisation one-shot

php
<?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
<?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
<?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 ».

Ordonnancement mémoire

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.

Aucune attente

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=N expose { 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) ou Shared\Mutex sur un tableau façon enum.
  • Attendre une transition. Les drapeaux ne bloquent pas. Associez-le à un Shared\Channel (ou à un Shared\Counter que vous interrogez par compareAndSet) lorsqu'un worker doit attendre que le drapeau s'inverse.
  • Compter des événements. Un drapeau n'est pas un compteur. Utilisez Shared\Counter pour 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.