Shared\Flag
OxPHP\Shared\Flag to atomiczna wartość logiczna obowiązująca w całym procesie — logiczny odpowiednik Shared\Atomic. Każda operacja jest bezblokadowa; dwa workery przełączające flagę współbieżnie nie mogą zaobserwować stanu pośredniego.
Przegląd
- Atomiczny bool. Pojedynczy bit stanu z operacjami
load/store/swap/compareAndSet. - Jawne porządkowanie pamięci. Każda operacja przyjmuje opcjonalny
Ordering, domyślnieSeqCst, dokładnie tak jakShared\Atomic. - Bezblokadowa. Wszystkie modyfikacje to pojedyncza operacja atomowa CPU. Bezpieczna przy rywalizacji.
- Współdzielona. Instancje żyją w rejestrze i można je przechowywać wewnątrz
Shared\Map, przekazywać przez przechwyceniauseitd.
Dokumentacja 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;
}| Metoda | Zwraca | Zastosowanie |
|---|---|---|
load |
bieżącą | Czysty odczyt. |
store |
void | Bezwarunkowe ustawienie na jawną wartość. |
swap |
poprzednią | Ustawia jawną wartość; wartość zwrotna mówi, czy ją zmieniłeś. swap(true) to test-and-set („czy wygrałem?"). |
compareAndSet |
zamieniono? | Jednorazowa inicjalizacja: powiedzie się tylko wtedy, gdy flaga miała oczekiwaną wartość. |
Przykłady
Wyłącznik awaryjny
<?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); // disableZwycięzca jednorazowej inicjalizacji
<?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.
}Zadziałanie bezpiecznika
<?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;
}Aby zbudować kompletny bezpiecznik, zwykle będziesz potrzebować Shared\Counter dla okna błędów oraz Shared\Flag dla stanu zadziałania — zresetuj flagę przez store(false), gdy okno ostygnie.
Opublikuj ładunek, a potem zasygnalizuj tańszym porządkowaniem
<?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');
}Semantyka i pułapki
swap zwraca poprzednią wartość, co jest najbardziej użytecznym wynikiem: „czy coś zmieniłem?" to $prev !== $new, a swap(true) to kanoniczny test-and-set. store zwraca void; jeśli potrzebujesz poprzedniej wartości, użyj swap.
compareAndSet to sposób na wyrażenie „pierwszy wygrywa". Zwykłe store(true) zawsze się powiedzie, więc nie potrafi wyrazić „nie nadpisuj, jeśli już ustawione".
Porządkowanie pamięci odpowiada Shared\Atomic. load odrzuca Release/AcqRel, store odrzuca Acquire/AcqRel, a $failure w compareAndSet odrzuca Release/AcqRel — każde z nich zgłasza InvalidOrderingException. Domyślne SeqCst jest zawsze bezpieczne.
Flaga nie blokuje. Jeśli musisz zaczekać na przejście, połącz ją z Shared\Channel lub użyj Shared\Once.
Wyjątki
| Wyjątek | Zgłaszany przez |
|---|---|
StaleHandleException |
Dowolną metodę na uchwycie, którego wpis w rejestrze usunięto. |
UninitializedException |
id() na obiekcie opakowującym, który nie zakończył __construct. |
InvalidOrderingException |
Ordering niedozwolone dla danej operacji (patrz wyżej). |
Obserwowalność
Zobacz Obserwowalność stanu współdzielonego. Szybkie odniesienia:
GET /__ox_shared/entry?id=Nudostępnia{ value: true|false, type: "Flag" }.- Miernik Prometheus
oxphp_shared_flag_value{flag_id="…"}(0 lub 1). - Metryki obejmujące cały rejestr uwzględniają Flag dzięki etykiecie
type="Flag".
Kiedy nie używać
- Logika wielostanowa. Flaga jest dwuwartościowa. Jeśli potrzebujesz stanów idle/busy/done lub dowolnej maszyny trójstanowej, sięgnij po
Shared\Counter(użyj całkowitoliczbowych wartości enum) alboShared\Mutexnad tablicą przypominającą enum. - Oczekiwanie na przejście. Flagi nie blokują. Połącz z
Shared\Channel(albo zShared\Counter, który odpytujesz przezcompareAndSet), gdy worker ma czekać, aż flaga się przełączy. - Zliczanie zdarzeń. Flaga nie jest licznikiem. Do zliczania używaj
Shared\Counter. - Stan całkowitoliczbowy. Jeśli przełącznik jest w istocie małą liczbą całkowitą, użyj bezpośrednio
Shared\Atomic.
Powiązane
- Stan współdzielony — przegląd i model myślowy.
- Shared\Atomic — odpowiednik int64, ten sam model porządkowania.
- Shared\Counter — gdy potrzebujesz czegoś więcej niż włącz/wyłącz.
- Shared\Once — gdy wartość obliczana raz jest bogatsza niż bool.
- Shared\Mutex — gdy przełączenie flagi musi zostać zatwierdzone wspólnie z innym stanem.