Shared\Flag

OxPHP\Shared\Flag はプロセス全体で共有されるアトミックなブール値であり、Shared\Atomic の bool 版です。すべての操作はロックフリーで、2 つのワーカーが同時にフラグを反転させても、中間状態が観測されることはありません。

概要

  • アトミックな bool。 load / store / swap / compareAndSet を備えた 1 ビットの状態です。
  • 明示的なメモリオーダリング。 すべての操作は省略可能な Ordering を受け取り、デフォルトは SeqCst です。Shared\Atomic とまったく同じです。
  • ロックフリー。 すべての変更は単一の CPU アトミック命令です。競合下でも安全です。
  • 共有可能。 インスタンスはレジストリ内に存在し、Shared\Map に格納したり、use キャプチャで渡したりできます。

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; }
メソッド 戻り値 ユースケース
load 現在値 純粋な読み取り。
store void 明示的な値を無条件に設定します。
swap 直前の値 明示的な値を設定します。戻り値で変更したかどうかがわかります。swap(true) は test-and-set(「自分が勝ったか?」)です。
compareAndSet 入れ替えたか? ワンショット初期化。フラグが期待した値だった場合のみ成功します。

キルスイッチ

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

ワンショット初期化の勝者

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. }

サーキットブレーカーのトリップ

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; }

完全なサーキットブレーカーを実装するには、通常は障害ウィンドウ用に Shared\Counter を、トリップ状態用に Shared\Flag を用意します。ウィンドウが落ち着いたら、store(false) でフラグをリセットします。

ペイロードを公開してから、より軽量なオーダリングでシグナルする

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'); }

セマンティクスと注意点

swap直前の 値を返します。これが最も有用な戻り値です。「何か変更したか?」は $prev !== $new で判定でき、swap(true) は test-and-set の定番です。storevoid を返します。直前の値が必要な場合は swap を使ってください。

compareAndSet は「最初の 1 つが勝つ」を表現する方法です。 単なる store(true) は常に成功するため、「すでに設定済みなら上書きしない」を表現できません。

メモリオーダリング

メモリオーダリングは Shared\Atomic と同じです。loadRelease/AcqRel を拒否し、storeAcquire/AcqRel を拒否し、compareAndSet$failureRelease/AcqRel を拒否します。いずれの場合も InvalidOrderingException を送出します。デフォルトの SeqCst は常に安全です。

待機はしない

Flag はブロックしません。状態遷移を待つ必要がある場合は、Shared\Channel と組み合わせるか、Shared\Once を使ってください。

例外

例外 発生元
StaleHandleException レジストリエントリが破棄されたハンドルに対するあらゆるメソッド。
UninitializedException __construct が完了していないラッパーに対する id()
InvalidOrderingException その操作で許可されていない Ordering(上記参照)。

可観測性

Shared の可観測性 を参照してください。クイックリファレンス:

  • GET /__ox_shared/entry?id=N{ value: true|false, type: "Flag" } を公開します。
  • Prometheus の oxphp_shared_flag_value{flag_id="…"} ゲージ(0 または 1)。
  • レジストリ全体のメトリクスは type="Flag" ラベルを通じて Flag をカバーします。

使うべきでないケース

  • 多状態のロジック。 Flag は 2 値です。idle/busy/done のような 3 状態のステートマシンが必要な場合は、Shared\Counter(整数の列挙値を使う)や、列挙型のような配列に対する Shared\Mutex を検討してください。
  • 状態遷移の待機。 Flag はブロックしません。ワーカーがフラグの反転を待つ必要がある場合は、Shared\Channel(または compareAndSet でポーリングする Shared\Counter)と組み合わせてください。
  • イベントのカウント。 Flag はカウンターではありません。集計には Shared\Counter を使ってください。
  • 整数の状態。 スイッチが実際には小さな整数である場合は、Shared\Atomic を直接使ってください。

関連

  • Shared State — 概要とメンタルモデル。
  • Shared\Atomic — int64 版の双子。同じオーダリングモデルです。
  • Shared\Counter — オン/オフ以上のものが必要なとき。
  • Shared\Once — 一度だけ計算される値が bool より複雑なとき。
  • Shared\Mutex — フラグの反転が他の状態と同時にコミットされる必要があるとき。