[BUG] Slash command dropdown causes multi-second UI freeze due to thread contention
[BUG] Slash command dropdown causes multi-second UI freeze due to thread contention
Summary
Opening the "/" slash command dropdown causes a 5-10 second UI freeze. strace analysis reveals this is caused by thread contention with busy-wait spinlocks rather than any external blocking call.
Environment
- Platform: Linux (WSL2)
- OS Version: Linux 6.6.87.2-microsoft-standard-WSL2
- Claude Code Version: 2.1.12 (native installer)
- Installation Method: Native (
curl -fsSL https://claude.ai/install.sh | bash)
Steps to Reproduce
- Start Claude Code with multiple MCP servers configured (Snowflake, Firecrawl, dbt-mcp, Neon, Context7)
- Have ~30 skills loaded (project + legacy commands)
- Type
/to open the slash command dropdown - Observe 5-10 second freeze before dropdown appears
Root Cause Analysis (via strace)
Attached strace to the Claude Code process during the freeze:
sudo strace -f -tt -T -p <PID> 2>&1 | tee /tmp/claude-trace.log
Finding 1: sched_yield() Storm (Busy Waiting)
The trace shows hundreds of sched_yield() calls in rapid succession:
[pid 100122] 14:19:05.755672 <... sched_yield resumed>) = 0 <0.000146>
[pid 100121] 14:19:05.755711 sched_yield( <unfinished ...>
[pid 100120] 14:19:05.755737 futex(0x73c3bd014190, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 100119] 14:19:05.755764 <... futex resumed>) = 0 <0.000903>
[pid 100124] 14:19:05.755791 futex(0x73c3c2e96de4, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 100123] 14:19:05.755817 sched_yield( <unfinished ...>
[pid 100122] 14:19:05.755857 sched_yield( <unfinished ...>
Problem: This indicates a userspace spinlock implementation. Instead of blocking (sleeping) on a mutex, threads spin in a tight loop, burning CPU time just checking if a lock is free.
Finding 2: High Futex Contention (EAGAIN)
Multiple futex calls return EAGAIN:
[pid 100124] ... futex resumed>) = -1 EAGAIN (Resource temporarily unavailable) <0.000208>
[pid 100123] ... futex resumed>) = -1 EAGAIN (Resource temporarily unavailable) <0.000168>
Problem: Threads are fighting over locks so aggressively that the state changes faster than the kernel can manage the wait queue.
Finding 3: Thread Convoy Effect
Sequential wakeup pattern instead of parallel processing:
PID 71330 reads an event (read(8, ...))
PID 71330 wakes PID 100118
PID 100118 wakes PID 100119
PID 100119 wakes PID 100120...
Problem: Threads serialize instead of running in parallel, defeating the purpose of the thread pool.
Finding 4: Redundant clock_gettime Calls
[pid 71330] ... clock_gettime(CLOCK_MONOTONIC_RAW, ...) = 0
[pid 71330] ... clock_gettime(CLOCK_MONOTONIC_RAW, ...) = 0
[pid 71330] ... clock_gettime(CLOCK_MONOTONIC_RAW, ...) = 0
Problem: 3-4 consecutive clock_gettime calls with no other syscalls between them suggests over-aggressive timing checks.
Impact
- UI completely unresponsive for 5-10 seconds
- CPU usage spikes during freeze (threads spinning)
- Affects productivity significantly when using slash commands frequently
Suggested Fix
Replace the yield-based spinlock pattern with proper blocking synchronization:
- Use
pthread_mutex_lock/pthread_cond_waitinstead ofsched_yield()loops - Or use Node.js's built-in async primitives that properly block
The current pattern:
// Current (bad): busy-wait spinlock
while (!lock_available) {
sched_yield(); // Burns CPU
}
Should become:
// Better: proper blocking
pthread_mutex_lock(&mutex);
while (!condition) {
pthread_cond_wait(&cond, &mutex); // Sleeps until signaled
}
pthread_mutex_unlock(&mutex);
Workaround (Partial)
Setting UV_THREADPOOL_SIZE=1 may reduce contention by forcing single-threaded operation, but this is not a proper fix and may slow down other operations.
Full strace Log
The complete strace output is available upon request (several MB of data showing the pattern repeating).
Additional Context
- Initially suspected the auto-updater was causing the freeze (based on debug logs showing
AutoUpdaterWrapperactivity) - strace analysis proved this was incorrect — the freeze is purely internal thread contention
- The issue occurs consistently when opening the "/" dropdown with many skills/MCP tools loaded
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗