Fix race conditions in mkdocs.yml file watcher callback#59
Merged
Conversation
jcouball
added a commit
that referenced
this pull request
Apr 24, 2026
- Fix error handling in AsyncSerializer do-while loop so coalesced pending calls still execute when fn() throws an error - Fix misleading JSDoc @returns to clarify coalesced calls share execution cycles - Tighten coalescing test assertion from '2-5' to exactly 2 - Add test verifying coalesced pending calls execute despite errors
Add AsyncSerializer class to serialize rapid mkdocs.yml config reloads, preventing race conditions when the file is saved multiple times quickly. The new AsyncSerializer: - Prevents concurrent execution of async operations - Coalesces multiple rapid calls into single re-execution - Ensures the most recent config is loaded - Guarantees diagnostics are refreshed with consistent data Following TDD methodology: 1. Created comprehensive unit tests with timing instrumentation 2. Implemented AsyncSerializer with do-while semaphore pattern 3. Integrated into extension.ts reloadMkdocsConfig callback 4. All tests pass with 100% coverage maintained Resolves #48
jcouball
force-pushed
the
fix/race-condition-mkdocs-watcher
branch
from
April 24, 2026 22:46
9b2de37 to
365316e
Compare
- Rename private fields to use underscore prefix per project conventions (_isExecuting, _pendingFn) - Replace hasPending boolean with _pendingFn storage so coalesced re-runs always execute the latest queued function, not the original
- Rewrite timing-dependent AsyncSerializer tests to use deterministic deferred promise barriers and inFlight counters instead of setTimeout/Date.now, eliminating flakiness under CI load - Fix reloadMkdocsConfig to read vscode.workspace.workspaceFolders live inside the callback rather than using the stale captured variable, so multi-root workspaces and folder changes are handled correctly
- Update AsyncSerializer JSDoc to document rejection behavior: the returned promise can reject, rethrowing the first error after all pending executions have been processed - Wrap reloadMkdocsConfig body in try/catch to prevent unhandled promise rejections from propagating to the extension host; errors are logged to the output channel and shown as a user-friendly message - Rename coalescing test to 'should coalesce multiple pending calls into a single additional execution' for accuracy
- Track last error instead of first error in AsyncSerializer: clear the error after each successful execution so the cycle only rejects if the final execution fails. A later coalesced success no longer causes a spurious rejection. - Update @returns JSDoc to precisely document the two-contract behavior: cycle-starters observe resolve/reject of the full cycle; coalesced callers only observe acknowledgement and do not see success/failure. - Update the coalesced-error test to reflect that promiseA now resolves (not rejects) when the coalesced re-run succeeds.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #48 - Prevents race conditions when
mkdocs.ymlis saved multiple times rapidly.Problem
The
reloadMkdocsConfigcallback inextension.tscould be called multiple times rapidly ifmkdocs.ymlwas saved multiple times quickly (e.g., during auto-save or rapid edits). SinceloadMkdocsConfig()is async, this created potential race conditions:forEachloop that refreshes diagnostics could operate on stale config data if a newer config load completes after the loop startsSolution
Implemented an
AsyncSerializerclass that uses a semaphore pattern to ensure only one config reload happens at a time, with coalescing of rapid events.Key Features
Implementation Details
New Files
src/asyncSerializer.ts- Pure business logic for serializing async operationssrc/test/unit/asyncSerializer.test.ts- Comprehensive unit tests with timing instrumentationModified Files
src/extension.ts- Integrated AsyncSerializer intoreloadMkdocsConfigcallbackTDD Approach
This fix was developed using strict Test-Driven Development:
Testing
Performance Impact
Related