Okay, so today I wanted to mess around with something I’ve been thinking about for a while: detecting deadlocks in a simple player count scenario. It’s one of those things that sounds easy but can get tricky fast.

Setting Up
First, I created a super basic Go program. The idea was to simulate players joining and leaving a game. I used goroutines to represent each player and channels to signal their actions (joining or leaving).
I whipped up a struct to hold the player count and a couple of methods to increment and decrement it. Pretty straightforward stuff:
- Created a
Game
struct with aplayerCount
field (integer). - Made an
AddPlayer
method that should increaseplayerCount
. - Made a
RemovePlayer
method that should decreaseplayerCount
.
The Problem (Deadlock Time!)
Now, to simulate the deadlock, I introduced mutexes. The goal was to make sure only one goroutine could modify the playerCount
at a time. Seemed reasonable, right? Wrong!
I added a to the Game
struct. Then, I wrapped the increment and decrement operations within Lock()
and Unlock()
calls. Here’s where I messed up (on purpose, of course, for science!):
I created a scenario where multiple goroutines would try to acquire the lock, but one of them would get stuck in an infinite loop before releasing the lock. It’s like one player grabbing the controller and then refusing to let go, ever.

Running and Observing
I fired up the program, and sure enough, it hung. My little player-counting simulation ground to a halt. The program just sat there, doing nothing. Classic deadlock.
I added a few print so I could see what was going on inside each.
The “Aha!” Moment (and the Fix)
Then, I put defer *()
at the start of each method that used the mutex. This is like a safety net – it tells Go, “Hey, no matter what happens in this function, make sure you unlock the mutex before you leave.” I did put some print, so that I could see when the mutex was actually being unlocked. It was so cool to watch!
I ran the program again, and this time, it worked! No more hanging. Players joined and left, and the count stayed accurate. It was a beautiful sight (well, as beautiful as a bunch of numbers changing in a terminal can be).
Conclusion
It was fun! It’s always the little things that get you, and forgetting to release a lock is a classic mistake. The key takeaway? Always, always make sure your locks are properly managed, especially when you’ve got multiple things happening at the same time.
