Skip to content

Fix tokenizer thread sleeping forever while grammar compiles on another thread (#131) - #132

Merged
danipen merged 3 commits into
masterfrom
fix-131-tokenizer-lost-wakeup
Oct 13, 2026
Merged

danipen merged 3 commits into
masterfrom
fix-131-tokenizer-lost-wakeup

Conversation

@danipen

@danipen danipen commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Fixes #131.

Problem

When a TMModel's tokenizer thread starts while its grammar is being compiled by another thread, it calls WaitHandle.WaitAny with no timeout. The pending _workAvailable signal (an AutoResetEvent) is consumed on the first loop, the next wait blocks, and the compiling thread never signals the model when it finishes. The lines stay invalid until some unrelated invalidation happens, so in an editor the document stays uncolored.

Also, UpdateTokensInRange skipped lines while the grammar was compiling without re-queuing them, so ForceTokenization during a compilation left them invalid.

Fix

  • While the grammar is compiling, wait with a short timeout (10 ms) so the worker notices when the compilation ends.
  • Re-queue the lines skipped in UpdateTokensInRange (same path as the existing stale-result branch).

Tests

  • First commit adds TMModel_Should_Tokenize_Lines_After_Grammar_Finishes_Compiling_On_Another_Thread, which fails deterministically on master. It uses an IGrammar wrapper with a controllable IsCompiling flag.
  • Second commit is the fix; the full suite passes.

🤖 Generated with Claude Code

danipen and others added 2 commits October 13, 2026 10:26
…#131)

The tokenizer thread waits indefinitely while the grammar is compiling on
another thread, and nothing signals it when the compilation ends, so the
model lines are never tokenized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er thread (#131)

- Poll with a short timeout while the grammar is compiling instead of
  waiting indefinitely, since the compiling thread never signals us.
- Re-queue lines skipped in UpdateTokensInRange while the grammar is
  compiling so they get tokenized afterwards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI 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.

🟡 Changes recommended

The concurrency test is nondeterministic, and the ForceTokenization requeue path lacks regression coverage.

2 open findings
What changed in this PR

Fixes tokenizer stalls while a shared grammar is compiling.

Changes:

  • Polls compilation state every 10 ms.
  • Requeues skipped lines and adds a regression test.
File Description
src/​TextMateSharp/​Model/​TMModel.cs Prevents indefinite waits and requeues lines.
src/​TextMateSharp.Tests/​Model/​TMModelTests.cs Tests tokenization after compilation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

tmModel.SetGrammar(grammar);

// let the tokenizer thread see IsCompiling and go to sleep
Thread.Sleep(300);

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.

Good catch, fixed in 0270b41. The wrapper now counts how many times IsCompiling was read while true, and the test waits for at least two reads (the first wait consumes the SetGrammar signal, the second one blocks) instead of sleeping. Verified it still fails against the unfixed TMModel.

Comment on lines +558 to +560
// Re-queue the line so it is tokenized once the compilation ends.
model.InvalidateLine(lineIndex);
return nextInvalidLineIndex;

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.

Added TMModel_ForceTokenization_Should_Tokenize_Lines_Skipped_While_Grammar_Is_Compiling in 0270b41: it marks a tokenized line invalid without queuing it, calls ForceTokenization while compiling is flagged, clears the flag and checks the line gets tokenized. Fails against the unfixed TMModel, passes with the fix.

- Wait until the tokenizer thread has checked IsCompiling twice before
  clearing it, instead of sleeping, so the test always reaches the
  blocking wait on the broken implementation.
- Add a test for lines skipped by ForceTokenization while the grammar
  is compiling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@danipen
danipen merged commit e0db45b into master Oct 13, 2026
5 checks passed
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.

TMModel tokenizer thread sleeps forever when its grammar is being compiled by another thread

2 participants