Absolutely. Here’s your second note: [[asymmetric_pthreads/02_pthread_deadlock_simulation]]
— documenting a deliberate deadlock trap for full internalization of mutex locking order.
🧨 [[asymmetric_pthreads/02_pthread_deadlock_simulation]]
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
void *thread1_fn(void *arg)
{
(void)arg;
printf("Thread 1 locking mutex 1 ... \n");
pthread_mutex_lock(&mutex1);
sleep(1);
printf("Thread 1 locking mutex 2 ... \n");
pthread_mutex_lock(&mutex2);
printf("Thread 1 acquired both mutexes ! \n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return (NULL);
}
void *thread2_fn(void *arg)
{
(void)arg;
printf("Thread 2 locking mutex 2 ... \n");
pthread_mutex_lock(&mutex2);
sleep(1);
printf("Thread 2 locking mutex 1 ... \n");
pthread_mutex_lock(&mutex1);
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
return (NULL);
}
int main(void)
{
pthread_t t1, t2;
pthread_create(&t1, NULL, thread1_fn, NULL);
pthread_create(&t2, NULL, thread2_fn, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return (0);
}
🔥 This File Will Deadlock
title: What Happens
- Thread 1 locks `mutex1` → sleeps → tries to lock `mutex2`
- Thread 2 locks `mutex2` → sleeps → tries to lock `mutex1`
- Both threads now **waiting forever**
🧠 What You Learn
-
Deadlocks occur when threads hold one lock and wait on another
-
Lock acquisition order matters
-
sleep()
makes race conditions predictable → great for learning -
You need a consistent global locking order policy
🛠️ How to Fix It
Ensure both threads acquire mutexes in the same order
// Both threads should always: lock mutex1 first, then mutex2
🧪 Expected Output (Before Freezing)
Thread 1 locking mutex 1 ...
Thread 2 locking mutex 2 ...
Thread 1 locking mutex 2 ...
Thread 2 locking mutex 1 ...
[Program freezes here — deadlock]
📚 Related Vault Notes
✅ Checklist
-
Simulates real-world deadlock
-
No crash, but infinite wait
-
Demonstrates the danger of inconsistent lock order
-
Teachable fix exists via ordering discipline
Ready to proceed with [[asymmetric_pthreads/03_pthread_exit_and_cleanup]]
?
✅ Copy that — I’m calling it.
We move forward into:
🔒 [[asymmetric_pthreads/02_pthread_deadlock_simulation]]
💥 “The Silent Mutual Lock That Never Crashes — But Never Returns”
🎯 Mission
This file is not about just seeing a deadlock.
It’s about feeling the illusion of progress — while your threads are locked in a death hug 🤝💀.
We will now dissect, refactor, and truth-bomb the file line by line.
Next message will contain:
-
Refactored
.c
file ✅ -
Asymmetric breakdown 🤯
-
Vault-formatted Obsidian entry 📦
Let’s begin.
Here is the full asymmetric breakdown of:
🔒 [[asymmetric_pthreads/02_pthread_deadlock_simulation]]
💥 “Two Locks. No Exit. Welcome to the World of Silent Death.”
🧬 Code: 02_pthread_deadlock_simulation.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
void *thread1_fn(void *arg)
{
(void)arg;
printf("🔐 Thread 1 locking mutex1...\n");
pthread_mutex_lock(&mutex1);
sleep(1);
printf("🔐 Thread 1 locking mutex2...\n");
pthread_mutex_lock(&mutex2);
printf("✅ Thread 1 acquired both locks!\n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return (NULL);
}
void *thread2_fn(void *arg)
{
(void)arg;
printf("🔐 Thread 2 locking mutex2...\n");
pthread_mutex_lock(&mutex2);
sleep(1);
printf("🔐 Thread 2 locking mutex1...\n");
pthread_mutex_lock(&mutex1);
printf("✅ Thread 2 acquired both locks!\n");
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
return (NULL);
}
int main(void)
{
pthread_t t1, t2;
printf("🚀 Launching both threads...\n");
pthread_create(&t1, NULL, thread1_fn, NULL);
pthread_create(&t2, NULL, thread2_fn, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("🏁 main() finished\n");
return (0);
}
🧠 What You Think You’re Learning
“Oh cool, threads need locks so they don’t mess up shared data!”
💣 What You’re Actually Learning (The Asymmetry)
👀 Surface Code | 💥 Reality |
---|---|
Each thread locks 2 mutexes | They lock them in opposite order |
sleep(1) is for show | It’s a deadlock trap trigger |
Code looks correct | No crash, just… 🧊 stuck. |
You join() both | You’ll wait forever — you just froze main() too. |
Only 2 threads | Yet they can halt an entire server if this logic leaks into prod. |
📉 Timeline of Doom
Thread 1 locks mutex1
Thread 2 locks mutex2
Thread 1 tries to lock mutex2... (blocked)
Thread 2 tries to lock mutex1... (blocked)
...forever...
Deadlock = mutual wait with no escape.
🧪 How to Observe It
$ ./a.out
🔐 Thread 1 locking mutex1...
🔐 Thread 2 locking mutex2...
🔐 Thread 1 locking mutex2...
🔐 Thread 2 locking mutex1...
🎯 Then: nothing.
🧊 CPU flatlines.
🧃 htop
shows threads stuck.
🐌 strace
hangs on futex()
.
🧠 Truth Bombs 💣
title: This Will Happen to You in Real Life
- In production, deadlocks don’t crash.
- They cause infinite spin, memory starvation, or client timeouts.
- And when you attach a debugger... they behave.
🛡️ How to Defuse It
🔢 1. Always lock mutexes in the same order across threads.
// both threads: lock mutex1, then mutex2
💬 2. Add timeout logic (e.g. pthread_mutex_timedlock()
)
🔍 3. Add detection logs: “Waiting for mutex2…”
✅ Checklist
🔍 Thing | Status |
---|---|
Uses two mutexes | ✅ |
Opposite lock order | ✅ |
Induces deadlock | ✅ |
No crash, silent freeze | ✅ |
Educationally asymmetric | ✅ |
Real-world observable? | ✅ — traceable with strace |
🔗 Related Vault Entries
🧠 Asymmetric Principle Here
You don’t need many threads to fail.
Just two mutexes, locked the wrong way — and you take down the system.
Shall we proceed to [[asymmetric_pthreads/03_pthread_exit_and_cleanup]]
, or do you want to patch this into a recovery variant first?