Commit 386eb74
Fix shutdown hang when background sync operations are in progress
When `Node::stop()` was called while background wallet sync operations
were actively running, the node would hang for up to 5+ seconds before
timing out and forcefully aborting tasks. In some cases, this could
result in an indefinite hang if blocking operations in spawned threads
couldn't be properly terminated.
## Root Cause
The background sync loop in `ChainSource::start_tx_based_sync_loop()`
used `tokio::select!` to multiplex between the stop signal and various
sync interval ticks. However, once a sync operation (e.g.,
`sync_lightning_wallet()`, `sync_onchain_wallet()`) began executing,
the select could not respond to the stop signal until that operation
completed.
These sync operations internally use `runtime.spawn_blocking()` for
I/O-heavy electrum/esplora calls, with timeouts of 10-20 seconds
(LDK_WALLET_SYNC_TIMEOUT_SECS, BDK_WALLET_SYNC_TIMEOUT_SECS). The
shutdown timeout (BACKGROUND_TASK_SHUTDOWN_TIMEOUT_SECS) is only 5
seconds, creating a race condition where:
1. Background sync starts a wallet sync (potential 10-20s operation)
2. User calls stop()
3. Stop signal is sent but sync operation continues
4. `wait_on_background_tasks()` times out after 5s and aborts
5. Blocking thread continues running, potentially causing hang
## Solution
This commit implements a biased nested `tokio::select!` pattern:
1. **Outer biased select**: The `biased` modifier ensures the stop
signal is always checked first before polling any interval ticks,
preventing new sync operations from starting after shutdown is
initiated.
2. **Inner nested selects**: Each sync operation is wrapped in its own
`tokio::select!` that can race the operation against the stop
signal. This allows cancellation even if a sync has just started.
With this approach, when `stop()` is called:
- The next loop iteration immediately sees the stop signal (biased)
- If a sync is in progress, it can be interrupted mid-operation
- Shutdown completes in milliseconds instead of seconds
## Testing
Added integration test `shutdown_during_background_sync` that enables
background sync with 2-second intervals, triggers manual sync, waits
for background sync to potentially start, then calls stop(). The test
verifies shutdown completes within 3 seconds (typically ~10ms).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>1 parent 1a134b4 commit 386eb74
2 files changed
+118
-7
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
320 | 320 | | |
321 | 321 | | |
322 | 322 | | |
| 323 | + | |
| 324 | + | |
323 | 325 | | |
324 | 326 | | |
325 | 327 | | |
| |||
328 | 330 | | |
329 | 331 | | |
330 | 332 | | |
331 | | - | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
332 | 346 | | |
333 | 347 | | |
334 | | - | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
335 | 361 | | |
336 | 362 | | |
337 | | - | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
342 | 380 | | |
343 | 381 | | |
344 | 382 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1860 | 1860 | | |
1861 | 1861 | | |
1862 | 1862 | | |
| 1863 | + | |
| 1864 | + | |
| 1865 | + | |
| 1866 | + | |
| 1867 | + | |
| 1868 | + | |
| 1869 | + | |
| 1870 | + | |
| 1871 | + | |
| 1872 | + | |
| 1873 | + | |
| 1874 | + | |
| 1875 | + | |
| 1876 | + | |
| 1877 | + | |
| 1878 | + | |
| 1879 | + | |
| 1880 | + | |
| 1881 | + | |
| 1882 | + | |
| 1883 | + | |
| 1884 | + | |
| 1885 | + | |
| 1886 | + | |
| 1887 | + | |
| 1888 | + | |
| 1889 | + | |
| 1890 | + | |
| 1891 | + | |
| 1892 | + | |
| 1893 | + | |
| 1894 | + | |
| 1895 | + | |
| 1896 | + | |
| 1897 | + | |
| 1898 | + | |
| 1899 | + | |
| 1900 | + | |
| 1901 | + | |
| 1902 | + | |
| 1903 | + | |
| 1904 | + | |
| 1905 | + | |
| 1906 | + | |
| 1907 | + | |
| 1908 | + | |
| 1909 | + | |
| 1910 | + | |
| 1911 | + | |
| 1912 | + | |
| 1913 | + | |
| 1914 | + | |
| 1915 | + | |
| 1916 | + | |
| 1917 | + | |
| 1918 | + | |
| 1919 | + | |
| 1920 | + | |
| 1921 | + | |
| 1922 | + | |
| 1923 | + | |
| 1924 | + | |
| 1925 | + | |
| 1926 | + | |
| 1927 | + | |
| 1928 | + | |
| 1929 | + | |
| 1930 | + | |
| 1931 | + | |
| 1932 | + | |
| 1933 | + | |
| 1934 | + | |
| 1935 | + | |
0 commit comments