Skip to content

fix(search): unwedge RunSearchAsync + propagate worker exceptions#35

Closed
OptimusPi wants to merge 1 commit into
masterfrom
claude/revive-avalonia-app-7W1Mw
Closed

fix(search): unwedge RunSearchAsync + propagate worker exceptions#35
OptimusPi wants to merge 1 commit into
masterfrom
claude/revive-avalonia-app-7W1Mw

Conversation

@OptimusPi
Copy link
Copy Markdown
Owner

Two HIGH-severity items from ISSUES.md fixed together, both regressions covered.

1. RunSearchAsync no longer deadlocks on the single-thread path

MotelySearch.cs:1205-1217 — when _threadCount == 1 the worker body ran synchronously inside Start(). The returned Task only completed after the search had already drained on the caller's thread, so await search.RunSearchAsync() on a sync context deadlocked. StartSearchThreads now off-threads every worker (single-thread path uses a dedicated background Thread instead of inlining), so the Task yields before the SIMD loop runs.

2. Worker exceptions are no longer swallowed

MotelySearch.cs:1224-1234 — multi-thread workers called RunWorkerBody with no try/catch. A worker throwing died silently, _completionSource never moved, and the caller hung forever. A WorkerCoordinator wraps every worker:

  • First exception is captured via Interlocked.CompareExchange.
  • The last worker out fires _completionSource.TrySetException.
  • OperationCanceledException under a requested cancel is treated as a clean exit.

The sync surface RunSearchUntilCompletion got the same treatment so it rethrows instead of pretending the search finished cleanly.

Tests

New Motely.Tests/MotelySearchReliabilityTests.cs:

  • RunSearchAsync_SingleThread_DoesNotDeadlockAwaitOnSameContext
  • RunSearchAsync_SingleThread_SurfacesWorkerException
  • RunSearchAsync_MultiThread_SurfacesWorkerException
  • RunSearchUntilCompletion_MultiThread_SurfacesWorkerException

All 424 tests pass (dotnet test Motely.Tests/Motely.Tests.csproj).

Test plan

https://claude.ai/code/session_01MGxcd6zZLNvNMWVFRAQ6oU


Generated by Claude Code

Two HIGH-severity items from ISSUES.md, fixed together with regression tests.

1. Single-thread RunSearchAsync used to run the worker body synchronously on
   the caller. The returned Task only completed *after* the search drained on
   the same thread, so `await search.RunSearchAsync()` on a sync context
   deadlocked. StartSearchThreads now off-threads every worker (single-thread
   path uses a dedicated background thread instead of inlining), so the Task
   yields before the SIMD loop runs.

2. Multi-thread workers used to call RunWorkerBody with no try/catch. A worker
   throwing died silently, `_completionSource` never moved, and the caller
   hung forever. A WorkerCoordinator now wraps every worker: first exception
   is captured, the last worker out fires TrySetException, and OperationCanceled
   under a requested cancel is treated as a clean exit. RunSearchUntilCompletion
   gets the same treatment so its sync surface rethrows instead of pretending
   the search finished clean.

Tests in Motely.Tests/MotelySearchReliabilityTests.cs cover both surfaces and
all of (1) single-thread no-deadlock, (2) single-thread exception propagation,
(3) multi-thread async exception propagation, (4) multi-thread sync exception
propagation. All 424 Motely.Tests pass.

https://claude.ai/code/session_01MGxcd6zZLNvNMWVFRAQ6oU
@OptimusPi OptimusPi closed this May 13, 2026
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.

2 participants