Угоди про іменування OxPHP\Shared\*

Простір імен OxPHP\Shared\* — це API паралелізму на рівні застосунку: Atomic, Counter, Flag, Map, Channel, Mutex, Once, Pool. Назви методів підпорядковуються єдиному набору правил, тож користувачі можуть передбачити API, не звертаючись до документації щодо кожного типу.

Цей документ є канонічним довідником. Нові примітиви, а також зміни в наявних, ПОВИННІ йому відповідати.

Правила

1. Читання значення — get()

Угода PHP. Використовується в Map::get(), Counter::get(), Once::get().

Atomic::load(Ordering $order = Ordering::SeqCst) — свідомий виняток: сама його наявність несе аргумент упорядкування й сигналізує, що читання є частиною контракту моделі пам’яті, відмінного від звичайного геттера.

2. Запис значення — set(), store() для атомарних типів

Map::set(), оновлення значення Mutex (за посиланням усередині withLock), Once::getOrInit(). Atomic::store(int $value, Ordering $order = Ordering::SeqCst) віддзеркалює load з тієї ж причини.

3. Кількість елементів — count(): int

Кожен контейнер, що відкриває свій поточний розмір, робить це під назвою count(): int. Channel додатково реалізує \Countable, тож count($ch) працює як нативна ідіома для елементів у черзі. Map відкриває count(): int як метод, але не реалізує \Countable — викликайте його безпосередньо. Pool не має count(); кількість живих слотів читайте з stats()->size():

php
$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)

Жодних size(), len() чи pending() — вони заборонені в публічному інтерфейсі, хоч би з якої мови походила м’язова пам’ять розробника.

4. Булевий геттер — префікс is*()

Channel::isClosed().

Жодних голих дієслів (test, check) і жодних предметно-специфічних назв (closed). Префікс is позначає чисте читання булевої властивості.

Тип, стан якого багатший за одне булеве значення, відкриває його через метод status(), що повертає enum, замість геттера is*() — так зроблено в RecvResult::status() у Channel та в Once::status(): Once\Status (Uninitialized/Pending/Ready/Poisoned). Використовуйте status(), коли відповідь має більше ніж два варіанти.

Mutex не відкриває isCorrupted() — пошкодження є стійким, невідновним і проявляється через CorruptedMutexException під час наступного захоплення. З такою перевіркою не можна зробити нічого корисного, крім повторного захоплення й перехоплення винятку.

5. Трихотомія політик очікування — try* / без суфікса / *Timeout

Блокувальні примітиви (Channel, Mutex) виражають політику очікування через назву методу, а не через перевантажений аргумент ?float $timeout:

Суфікс Поведінка Приклади
try* Неблокувальний; одразу повертає варіант невдачі. Channel::trySend, Channel::tryRecv, Mutex::tryWithLock
(без суфікса) Блокується назавжди (або доки файбер запиту не скасовано). Channel::send, Channel::recv, Mutex::withLock
*Timeout Обмежене очікування. Приймає обов’язковий int $ms > 0. Channel::sendTimeout, Channel::recvTimeout, Mutex::withLockTimeout

Трихотомія виносить три неоднозначні політики (null = назавжди, 0 = спроба, додатне значення = обмежено) з одного параметра в три методи із самодокументованими назвами.

Note

Аргумент $ms у методах *Timeout є строго додатним. Нуль, від’ємні, нецілі та відсутні значення спричиняють OxPHP\Shared\TypeException на рівні мосту.

Операції з умовним успіхом живуть у Map під назвою setIfAbsent, а не try*: Map::setIfAbsent фіксує запис лише тоді, коли ключ був відсутній (аналогічно до HashMap::try_insert), і повертає наявне значення або null, якщо вставлення відбулося. Назва setIfAbsent зарезервована саме для цієї семантики; не використовуйте її деінде.

Те, як метод try* повідомляє про невдачу, залежить від типу: Channel повертає типізований Result, Mutex кидає ContentionException, Pool::tryAcquire() повертає null, коли пул насичений, а Once::trySet() повертає false, коли комірка вже в стані Ready або Pending.

6. Порівняння з обміном — compareAndSet()

Atomic::compareAndSet(), Flag::compareAndSet(). Завжди повертає bool (обмін відбувся або ні).

7. Заміна з поверненням попереднього — swap()

Atomic::swap() для int, Flag::swap() для bool. Повертає попереднє значення.

8. Атомарний RMW з поверненням попереднього — префікс fetch*()

Atomic::fetchAdd(), fetchSub(), fetchAnd(), fetchOr(), fetchXor().

Префікс fetch кодує контракт повернення: значення до операції. На противагу цьому Counter::add() повертає нове значення (агрегований лічильник у стилі LongAdder).

Додаючи нові RMW-методи, спершу оберіть контракт, а потім назву:

  • повернення попереднього значення → fetchVerb(args)
  • повернення нового значення → просто verb(args)

Не змішуйте.

9. Скидання до початкового стану — clear()

Map::clear() — спорожнює контейнер; повертає кількість видалених записів.

Counter не має clear() — його віконним скиданням є set(0). Counter::set() — задокументований виняток, що повертає попереднє значення (а не void): це атомарний обмін, а set(0), що зчитує попередню суму, — це ідіома LongAdder sumThenReset. (Atomic називає ту саму операцію swap(); Counter зберігає set, бо set($n) природно читається для початкового заповнення й роботи з вікнами.)

10. Ідентичність у реєстрі — id(): int

Кожен екземпляр Shared\* відкриває id(): int для журналів та ендпоінта спостережуваності /__ox_shared/entry?id=<id>.

Шпаргалка

Поняття Канонічна назва Приклади
Читання значення get() Map::get, Counter::get
Читання атомарного значення load($order) Atomic::load
Запис значення set() Map::set
Запис атомарного значення store($v, $order) Atomic::store
Кількість елементів count(): int Map::count, Channel::count
Булева властивість is*(): bool Channel::isClosed
Умовна вставка setIfAbsent($k, $v) Map::setIfAbsent
Неблокувальне очікування try*() Channel::trySend, Mutex::tryWithLock
Безстрокове очікування дієслово без суфікса Channel::send, Channel::recv, Mutex::withLock
Обмежене очікування *Timeout(int $ms) Channel::sendTimeout, Mutex::withLockTimeout
Порівняння з обміном compareAndSet() Atomic::compareAndSet
Обмін, повертає попереднє swap() Atomic::swap, Flag::swap
Атомарний RMW, повертає попереднє fetch*() Atomic::fetchAdd
Атомарний RMW, повертає нове дієслово без суфікса Counter::add
Скидання до початкового стану clear() Map::clear
Ідентифікатор у реєстрі id(): int кожен тип Shared\*

Додавання нового типу Shared\*

Пропонуючи новий примітив, заповніть цей чек-лист перед злиттям:

  • Кожен метод відповідає рядку шпаргалки або має ADR, що пояснює виняток (див. Atomic::load/store та Counter::set вище).
  • Якщо тип містить колекцію значень, він реалізує \Countable і відкриває count(): int.
  • Методи читання — це get або load (лише для атомарних).
  • Булеві геттери використовують префікс is*.
  • Варіанти політики очікування дотримуються трихотомії try* / без суфікса / *Timeout(int $ms). Варіант *Timeout приймає int $ms > 0 і відхиляє нульове / від’ємне / нецілочислове значення з TypeException. Методи try* політики очікування або повертають типізований Result, або кидають предметний виняток — ніколи не кодують невдачу через null. Pool::tryAcquire(), який повертає null, коли пул насичений, цьому не відповідає. Операції з умовним успіхом дотримуються окремого іменування setIfAbsent замість try*.
  • Жодних len, size, pending, test чи інших довільних назв.
  • Предметно-специфічні дієслова (evict, drain, flush тощо) з’являються лише тоді, коли жоден канонічний запис шпаргалки не покриває поняття.

Назви спостережуваності

Інтерфейс для операторів — назви метрик Prometheus і JSON за адресою /__ox_shared/entry?id=<id> — є окремим контрактом, незалежним від PHP API, бо їхнє перейменування ламає дашборди й правила алертів. Кількість елементів Channel чи Pool — це oxphp_shared_channel_count або oxphp_shared_pool_count на /metrics і count у його JSON type_specific. Для Channel це число, яке повертає Channel::count(); для Pool — живі слоти, тобто число, яке повертає Pool::stats()->size().

Вилучено в 0.13.0

Попередні назви — oxphp_shared_channel_pending, oxphp_shared_pool_size, а також ключі JSON pending і size — більше не публікуються. Дашборди й правила алертів, досі написані під них, не знаходять жодних рядів, тож порогові алерти на них ніколи не спрацюють.

Стабільність

Ці правила є частиною контракту OxPHP\Shared\* 1.0. Після релізу 1.0 перейменування є несумісними змінами й потребують циклу застарівання. До 1.0 правила так само обов’язкові — нові методи, що їх порушують, буде відхилено під час рев’ю.