Deadlocks in Windows: how to spot them and how to avoid them

KaRzye14 August 20264 min readUpdated 4 October
Dark UltimateMove banner reading When two threads wait forever, with two blocked locks in a cycle

The game freezes for two seconds in the middle of a duel. FPS reads 240 and the picture stands still. The server drops a tick even though the CPU is idling. Behind some of these cases sits a deadlock, a thread lock-up. This article shows how to recognise them and how to get rid of them.

What a deadlock is

Deadlock from Valve… we write about it a lot - unfortunately this time we mean something less colourful - a system deadlock. It happens when two threads wait on each other for resources they have already locked. Thread A holds lock 1 and waits for lock 2. Thread B holds lock 2 and waits for lock 1. Neither gives way, so both stand still until the process dies.

From the player perspective there is no load to see. The CPU has headroom, the GPU has headroom, and still nothing moves.

Where it hurts in games and on servers

AreaExample in gamesConsequences
ClientA lock on the render thread while a frame waits for network dataA stutter or a black screen at high FPS
ServerGame logic waits for a result from another thread, for example from AIDelays in state synchronisation, a drop in tick-rate
MatchmakingThe player matching algorithm waits for data locked by another processLonger queues, more timeouts when a map is assigned
Live session, remoteRemote PC optimization for gaming, measured before and after

We connect to your PC remotely, save a restore point, then work down through BIOS, power, drivers and input. The same benchmark at the end shows how much of it landed. For six months after the session we stay with you on Discord.

Pick a package →from 150 zł · 60 min+

How to detect deadlocks in Windows

  1. Task Manager, Details tab. Right-click the process and select Analyze wait chain. The window shows what the selected process is waiting on.
  2. Process Explorer (Sysinternals). W Open Handles look for the names of mutexes, files and COM objects held too long.
  3. PerfMon. Counters Thread State and Thread Wait Reason, that is, the thread state and the reason it is waiting. A value of 5 in Thread State means waiting.
  4. Process dump and WinDbg. Save a dump of the hung process and go through the stacks with the command ~* k. Deadlocked threads sit on the same WaitForSingleObject for the whole dump.

Where they come from

  • A different lock ordering in different threads
  • An I/O operation performed inside a critical section
  • Mixing COM apartments, that is, synchronous calls between STA and MTA
  • Priority inversion, when a low priority thread holds a lock a high priority thread needs

How to avoid them

StrategyWhat to doExample code (C++/Win32)
Lock orderingSettle on one lock ordering and stick to it across the whole codebase, for example alphabetically by name.void AcquireAll(std::vector<MutexHandle>& locks){ std::sort(locks.begin(), locks.end(), [](auto& a, auto& b){ return a.name < b.name; }); for(auto& l : locks) WaitForSingleObject(l.handle, INFINITE); }
Try-lock with a timeoutIf you do not get the lock within the given time, release what you already hold and start again.bool TryLock(MutexHandle& m){ return WaitForSingleObject(m.handle, 500) == WAIT_OBJECT_0; }
Do not block the main threadKeep the UI and render threads free. Do I/O asynchronously.// asynchronous read via an overlapped structure ReadFile(hFile, buf, size, NULL, &ov); // no blocking calls in the critical section
SRWLOCK instead of a mutexMany readers work in parallel, and only writes get exclusivity.SRWLOCK lock = SRWLOCK_INIT; AcquireSRWLockShared(&lock); ReleaseSRWLockShared(&lock);
COM apartment hygieneUI in STA, server logic in MTA. Avoid synchronous calls between them.CoInitializeEx(NULL, COINIT_MULTITHREADED);
A watchdog that detects cyclesCheck the wait graph every few seconds. A cycle means a deadlock, so restart the thread or the application.Depth-first search (DFS) over the wait-for graph

Concrete cases from game servers

ProblemSolution
Matchmaking waits on the database (SQL Server)Shorten transactions, review query plans, enable READ COMMITTED SNAPSHOT.
The AI thread holds a mutex for the whole computationMove the computation outside the critical section and release the lock right after writing the result.
Game state synchronisation at a high server tick-rateUse SpinLock instead of Mutex, but keep the critical section within a few microseconds.

What the player gets out of it

  • Shorter delays. Removing a deadlock takes the milliseconds you will not see in any benchmark off the server response time.
  • A stable session. The client stops freezing at random moments, and the FPS graph stops having holes in it.
  • Better scalability. The server handles more players on the same hardware.

Checklist after rolling out the changes

  • Task Manager. Under heavy load no process waits forever. Check it with the Analyze wait chain.
  • PerfMon. Thread State of the game threads does not stay at 5 (Wait) for the whole measurement.
  • Server. Average server tick-rate matching its configured value, with no dropped ticks.
  • Profiler. Intel VTune or AMD uProf shows no long critical sections.

So what you need?

A deadlock consumes no resources, so you will not see it on any load graph. Look for it where the threads stand still: in Task Manager, in Process Explorer and in stack dumps.

Three habits are enough to stop producing them. Settle on one lock ordering. Keep I/O out of critical sections. On the server reach for lock-free structures, and on the client for asynchronous I/O. You see the result in the ping, in frame stability and in the number of players the server holds without choking.


Related articles


Frames dropping exactly when the fight starts?

That drop lands in the one second that decides the round, and no graphics preset fixes it. We tune your PC remotely: Windows, drivers, BIOS and network. You measure it yourself, in your own game, before and after, with us watching.

See PC optimization →Six months of support after the session. Not sure it is worth it? Ask for a free check on our Discord first.
KaRzye Runs the research and every session at UltimateMove. FACEIT Level 10 in CS2, Immortal in Valorant. Twenty years in BIOS, memory and drivers. Watch him play.

Leave a Comment

KaRzye - Gaming logo - Twitchtv channel

"KaRzye" - CS2 Faceit lvl 10, Immortal Valorant coach, and PC optimization expert with over 15 years of experience. He has been overclocking and optimizing gaming computers since 2006.

Current credentials:

Ultimate Deadlock Replay Analyzer
Deadlock Stream Overlay

Shopping Cart