Skip to content

fix(pageout): SDK-константы, честный дефолт scratch, идентичность pid в recovery - #67

Open
froggychips wants to merge 1 commit into
mainfrom
fix/pageout-honesty
Open

froggychips wants to merge 1 commit into
mainfrom
fix/pageout-honesty

Conversation

@froggychips

Copy link
Copy Markdown
Owner

Ревью 2026-09-19, поток «pageout». Факты — в новом ADR-0018.

Что не так было

  • Pageout.swift:278: kVMBehaviorPageout = 6 — по SDK это VM_BEHAVIOR_FREE («free without write-back»). При удачном task_for_pid machVM стирал содержимое writable-страниц чужого процесса. Настоящий VM_BEHAVIOR_PAGEOUT = 11 и помечен «development only».
  • Pageout.swift:272: команда 1 = GET_PRIORITY_LIST, а не SET_PRIORITY_PROPERTIES (= 2). И главное: memorystatus_control требует root или com.apple.private.memorystatus (XNU kern_memorystatus.c:9193) — стратегия jetsam для LaunchAgent недостижима. bench/baseline.json это и показывал: jetsamFailed: 1 в каждом снимке.
  • FrozenPidsStore.recover() бил SIGCONT/SIGKILL по pid без проверки, что это тот же процесс.
  • VortexActor.freezeProcess писал журнал ПОСЛЕ SIGSTOP; thawAll сносил и записи живых воркеров.

Что сделано

  • Константы через SDK-символы; machVM → .failed, если ни один регион не принял behavior; jetsam при EPERM называет причину; дефолт PageoutChain.scratch.
  • FrozenPidsStore.Entry.startTime (proc_pidinfo) + проверка живости/пути/времени старта перед сигналом; clearFrozen() не трогает воркеров; legacy-файлы без поля читаются.
  • VortexActor: журнал до SIGSTOP, откат при провале kill, thawGeneration закрывает окно «add → thaw → SIGSTOP».
  • ADR-0018 (en/ru), ADR-0007 помечен «superseded in part», индекс ADR дополнен (0016–0018), README честно описывает pageout.

Зависимость: дефолт FroggyConfig.pageoutStrategy = .scratch живёт в #daemon-hardening (та же серия). До его мержа рабочий демон продолжит пробовать jetsam и падать в scratch — как и раньше.


Проверка. Собрано локально (swift build --product FroggyDaemon / froggy / FroggyAudioWorker, только CommandLineTools) без ошибок и предупреждений в изменённых файлах. Тесты не компилировались и не запускались: на машине нет XCTest (нет Xcode), self-hosted раннер jabbook-air-m3 offline, ci.yml на macos-latest давно startup_failure. Diff прошёл ревью Codex, замечания учтены. Перед мержем нужен make test на машине с Xcode.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wt32A3w8X6bMZAXKSVmsCv

…id identity in recovery (review 2026-09-19)

Pageout.swift
- kVMBehaviorPageout был 6 = VM_BEHAVIOR_FREE («free without write-back»):
  при удачном task_for_pid machVM уничтожал бы содержимое writable-страниц
  чужого процесса. Теперь символ SDK VM_BEHAVIOR_PAGEOUT (= 11, development
  only); VM_REGION_BASIC_INFO_64 тоже из SDK.
- kMemorystatusCmdSetPriorityProperties был 1 = GET_PRIORITY_LIST; верное
  значение 2. При этом memorystatus_control требует root или entitlement
  com.apple.private.memorystatus (XNU kern_memorystatus.c) — jetsam для
  LaunchAgent недостижим, EPERM теперь объясняется словами.
- MachVMPageoutImpl: 0 принятых регионов → .failed (раньше .success без
  единой страницы), цепочка откатывается дальше.
- Дефолт PageoutChain.preferred → .scratch (ADR 0018). Doc-комментарии
  стратегий переписаны честно.

FrozenPidsStore.swift / VortexActor.swift
- Entry.startTime (proc_bsdinfo.pbi_start_tvsec) заполняется автоматически;
  recover() шлёт SIGCONT/SIGKILL только если pid жив, время старта (если
  записано) и путь бинаря совпадают — иначе «pid reused, skipping».
  recoverDetailed() отдаёт разбивку thawed/killed/skipped. Старые файлы
  без поля читаются.
- clearFrozen(): thawAll снимает только записи приложений, записи воркеров
  переживают.
- freezeProcess: журнал ДО SIGSTOP, при провале kill запись снимается;
  errno читается до await.

Docs: ADR 0018 (en+ru) с фактами из SDK/XNU/baseline.json; ADR 0007 помечен
частично superseded; индекс ADR дополнен 0016–0018; README (en+ru) —
буллет про pageout и пример конфига переписаны под реальность.

Tests: FrozenPidsStore — идентичность процесса (чужой startTime / чужой путь
/ полное совпадение / legacy JSON), clearFrozen; PageoutChain — дефолт
scratch. Локально не компилировались (на машине нет XCTest).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wt32A3w8X6bMZAXKSVmsCv

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ff522dc14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


public init(
preferred: PageoutStrategy = .jetsam,
preferred: PageoutStrategy = .scratch,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate the scratch default to FroggyConfig

The production daemon does not use this initializer default: Sources/FroggyDaemon/main.swift explicitly passes config.pageoutStrategy, while FroggyConfig() and its missing-field decoder fallback still default to .jetsam (Config.swift:121,226). Consequently, fresh or value-omitting configurations continue attempting the privileged jetsam strategy and incrementing its failure counters before falling back, contrary to the README and ADR changes in this commit; update the configuration default in the same change.

Useful? React with 👍 / 👎.

Comment on lines +108 to +110
guard thawGeneration == generationBefore else {
await pidStore?.remove(pid: pid)
throw VortexError.freezeAborted(pid: pid, reason: "thaw during freeze")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recheck the thaw generation before reporting success

This guard only detects a thaw during pidStore.add; if thawAll or thawProcess runs after SIGSTOP while pageout.pageout or ranker.recordFreeze is awaited, it increments the generation, resumes and removes the PID, but freezeProcess later returns success. The awaiting VortexCoordinator.freezeTier then inserts that now-running PID into its frozen tier set after the thaw cleared the sets, causing subsequent pressure handling to skip it as already frozen. Recheck the captured generation before returning success so this overlap is reported as an aborted freeze.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant