Skip to content

Make seconv conversion cancellation-aware and serialize global state - #109

Draft
Blackspirits wants to merge 7 commits into
upl/review-base-c77cfrom
fix/seconv-cancellation-serialization-c77c
Draft

Make seconv conversion cancellation-aware and serialize global state#109
Blackspirits wants to merge 7 commits into
upl/review-base-c77cfrom
fix/seconv-cancellation-serialization-c77c

Conversation

@Blackspirits

@Blackspirits Blackspirits commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Summary

This is prerequisite hardening for long-lived/concurrent callers such as the proposed seconv mcp server.

SubtitleConverter currently mutates process-wide state during a run:

  • Configuration.Settings.General.CurrentFrameRate while loading frame-based formats;
  • static SpellCheckConfig delegates for Fix Common Errors/OCR;
  • translation prompt/model settings through AutoTranslateRunner.

A CLI invocation normally has only one conversion, but a long-lived server can invoke multiple converters concurrently and cross-contaminate those globals.

The converter also had no effective cancellation contract: Spectre passes a CancellationToken to ConvertCommand, but the command discarded it, and translation was explicitly invoked with CancellationToken.None.

This change:

  • serializes SubtitleConverter runs behind a process-wide async gate while conversion still depends on mutable global libse state;
  • adds an optional CancellationToken to ConvertAsync without breaking existing callers;
  • passes the CLI command token into the converter;
  • propagates cancellation through files, tracks, image-preservation stages, Task.Run, and auto-translation;
  • rethrows caller cancellation at every conversion error boundary rather than recording it as an ordinary failed file;
  • adds checkpoints around synchronous native/bitmap stages that cannot themselves accept a token;
  • adds a regression test proving a pre-cancelled conversion throws before writing output.

Scope / limitation

Cancellation is cooperative. Synchronous legacy/native helpers without a token cannot be pre-empted inside the call; cancellation is observed before those stages and between work units. A completed synchronous write is not retroactively reported as cancelled, avoiding retry ambiguity after side effects already exist. The serialization gate is intentionally conservative until the remaining process-global settings become per-conversion state.

Validation

  • based on upstream c77c4b428649ce7030e6cf218b4d7c384b955232
  • changed files: SubtitleConverter.cs, ConvertCommand.cs, one focused cancellation test
  • final HEAD: f83e30793665214f054f411feb0c2254b8b68d8a
  • restore and build passed
  • SeConvTests passed on the first run: 476 passed, 2 skipped, 0 failed (478 total)
  • LibUiLogicTests passed on the first run: 873 passed, 0 skipped, 0 failed
  • LibSETests passed on the first run: 2,008 passed, 0 skipped, 0 failed
  • the first UITests run hit one unrelated UI/keymap flake: NativeKeymapShiftDeleteCutTests.Delete_WithoutShift_StillJustDeletes_InTheSyntaxTextEditor
  • the workflow retry in a fresh process passed the complete suite; UITests: 5,151 passed, 9 skipped, 0 failed (5,160 total)
  • retry was used only because of that unrelated UI flake
  • final validation run: #34791238162
  • audit draft only; no merge/promotion intended

AI assistance: ChatGPT was used to audit seconv cancellation propagation and concurrency hazards exposed by long-lived MCP callers.

@Blackspirits Blackspirits left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Independent adversarial re-check: conversion remains backwards-compatible through an optional CancellationToken, the CLI now forwards its token, caller cancellation is rethrown through every converter error boundary, and conversion runs are serialized while libse/seconv still mutate process-global settings. Cancellation around synchronous legacy/native work is cooperative rather than pre-emptive, which is documented in the PR. Full CI #34788562083 passed restore, build and the complete solution suite on the first run; SeConvTests reported 476 passed, 2 skipped and 0 failed, and UITests 5,151 passed, 9 skipped and 0 failed. Retry was not used. No blocker identified. Keep draft; no merge performed.

@Blackspirits Blackspirits left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Final adversarial re-check on f83e307: cancellation is propagated through the converter and caller cancellation escapes every conversion error boundary. The final correction also avoids checking for cancellation after a completed synchronous write/extraction, so an already-produced output is not reported as cancelled and accidentally retried. CI #34791238162 passed restore/build. SeConvTests (476/2/0), LibUiLogicTests (873/0/0) and LibSETests (2008/0/0) passed on the first run. The first UITests run had one failure in NativeKeymapShiftDeleteCutTests.Delete_WithoutShift_StillJustDeletes_InTheSyntaxTextEditor, a UI/keymap test outside all three changed seconv files; the fresh-process retry passed UITests 5151/9/0 and the entire suite. This is consistent with an unrelated UI flake, not a #109 regression. No blocker identified. Keep draft; no merge performed.

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