<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: prabhu ponnambalam</title>
    <description>The latest articles on DEV Community by prabhu ponnambalam (@prabhu_ponnambalam_67867a).</description>
    <link>https://dev.to/prabhu_ponnambalam_67867a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3746423%2Fa319fcae-e352-48b7-93be-b3a5166128b1.png</url>
      <title>DEV Community: prabhu ponnambalam</title>
      <link>https://dev.to/prabhu_ponnambalam_67867a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prabhu_ponnambalam_67867a"/>
    <language>en</language>
    <item>
      <title>Ship Safer Code: The GitHub Actions Patterns That Actually Matter</title>
      <dc:creator>prabhu ponnambalam</dc:creator>
      <pubDate>Sun, 15 Mar 2026 22:11:06 +0000</pubDate>
      <link>https://dev.to/prabhu_ponnambalam_67867a/ship-safer-code-the-github-actions-patterns-that-actually-matter-1omd</link>
      <guid>https://dev.to/prabhu_ponnambalam_67867a/ship-safer-code-the-github-actions-patterns-that-actually-matter-1omd</guid>
      <description>&lt;h2&gt;
  
  
  The Problem With Most CI Guides
&lt;/h2&gt;

&lt;p&gt;You search "GitHub Actions unit tests", find a guide, copy the YAML, and it works — until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A docs-only PR triggers a 20-minute build&lt;/li&gt;
&lt;li&gt;Tests pass in CI but crash in production&lt;/li&gt;
&lt;li&gt;One flaky test fails randomly and blocks your whole team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are real problems. Here's how to solve them.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Path Filtering — Skip Builds When Nothing Changed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82rqfa8tuhgoi907r7q1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82rqfa8tuhgoi907r7q1.png" alt="skip tests when no code changed" width="600" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Detect changes&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changes&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dorny/paths-filter@v3&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;code:&lt;/span&gt;
        &lt;span class="s"&gt;- '**/*.cpp'&lt;/span&gt;
        &lt;span class="s"&gt;- '**/*.h'&lt;/span&gt;
        &lt;span class="s"&gt;- '**/*.hpp'&lt;/span&gt;
        &lt;span class="s"&gt;- '**/CMakeLists.txt'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.changes.outputs.code == 'true'&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make run-tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real impact:&lt;/strong&gt; A typo fix in &lt;code&gt;README.md&lt;/code&gt; used to burn 20 minutes of runner time. Now it passes in under 5 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Build and Test Inside Docker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiknai2u9b3kofg2rufc9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiknai2u9b3kofg2rufc9.png" alt="GitHub Actions runner box" width="700" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Runner environment drift is the silent killer of reproducibility. The Ubuntu version changes, a system library gets updated, and suddenly your green CI is red for no reason you can explain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Docker image with tests enabled&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;docker build . -f Dockerfile -t myapp-ci \&lt;/span&gt;
      &lt;span class="s"&gt;--build-arg WITH_TESTS=true&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests inside container&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;mkdir -p ./test-output&lt;/span&gt;
    &lt;span class="s"&gt;docker run --rm \&lt;/span&gt;
      &lt;span class="s"&gt;-v ./test-output:/app/build/Testing/Temporary \&lt;/span&gt;
      &lt;span class="s"&gt;myapp-ci \&lt;/span&gt;
      &lt;span class="s"&gt;ctest --test-dir /app/build --output-on-failure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why mount &lt;code&gt;./test-output&lt;/code&gt;?&lt;/strong&gt; When tests fail, you need the logs. Without the mount, they disappear when the container exits.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Run ASAN and TSAN — Your Code Isn't as Safe as You Think
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqy5j1ce5106zs6axsw95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqy5j1ce5106zs6axsw95.png" alt="sanitizer" width="700" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most C++ bugs that reach production wouldn't make it past:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AddressSanitizer (ASAN)&lt;/strong&gt; — buffer overflows, use-after-free, memory leaks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ThreadSanitizer (TSAN)&lt;/strong&gt; — data races, deadlocks
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/workflows/test.yml&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;

  &lt;span class="na"&gt;asan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/workflows/test.yml&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;asan&lt;/span&gt;

  &lt;span class="na"&gt;tsan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;asan&lt;/span&gt;          &lt;span class="c1"&gt;# stagger — both are CPU-heavy&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/workflows/test.yml&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tsan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Don't run ASAN and TSAN simultaneously.&lt;/strong&gt; They compete for the same runner resources and you'll get false failures from starvation, not real bugs. Use &lt;code&gt;needs:&lt;/code&gt; to sequence them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Retry Only the Failing Tests — Not the Whole Suite
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdi5udfwfzeqxmvz6yd2s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdi5udfwfzeqxmvz6yd2s.png" alt="Retry failed tests" width="700" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flaky tests exist. Denying this costs your team hours per week. The wrong fix is marking tests &lt;code&gt;DISABLED_&lt;/code&gt; or ignoring them. The right fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ctest --test-dir build --output-on-failure --parallel $(nproc)&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retry Failed Tests&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;failure()&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;ctest --test-dir build \&lt;/span&gt;
      &lt;span class="s"&gt;--rerun-failed \&lt;/span&gt;
      &lt;span class="s"&gt;--output-on-failure \&lt;/span&gt;
      &lt;span class="s"&gt;--repeat until-pass:3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--rerun-failed&lt;/code&gt; is a CTest built-in. It reads the last run's results and only executes what failed. A 20-minute full suite retry becomes a 90-second targeted retry.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Debug Verbose Output — Without Polluting Normal Runs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftonlb35bmo190yxk0q4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftonlb35bmo190yxk0q4w.png" alt="verbose toggle" width="700" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verbose test output in every run is thousands of lines of noise. But when you're debugging at 2am, you need every byte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runner.debug != '1'&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ctest --test-dir build --output-on-failure&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests (verbose)&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runner.debug == '1'&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ctest --test-dir build --verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable debug mode: &lt;strong&gt;GitHub Actions → Re-run jobs → Enable debug logging&lt;/strong&gt;. No YAML changes, no new commits. Just flip a switch.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Cancel Redundant Runs With One Config Block
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxid1395h319xw2mdq1g4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxid1395h319xw2mdq1g4.png" alt="concurrency cancel" width="700" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You push a fix, realize you forgot something, push again. Now you have two CI runs for the same branch. The first one is waste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two lines at the top of every workflow. The new push kills the old run immediately. Zero cost, zero thought, instant win.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CI is code.&lt;/strong&gt; Treat it with the same discipline: no waste, informative failures, easy to maintain.&lt;/p&gt;




</description>
      <category>automation</category>
      <category>cicd</category>
      <category>devops</category>
      <category>github</category>
    </item>
    <item>
      <title>The Deadlock Detective: GDB Techniques for Solving Thread Crimes in Real-Time</title>
      <dc:creator>prabhu ponnambalam</dc:creator>
      <pubDate>Mon, 02 Feb 2026 20:22:33 +0000</pubDate>
      <link>https://dev.to/prabhu_ponnambalam_67867a/the-deadlock-detective-gdb-techniques-for-solving-thread-crimes-in-real-time-il0</link>
      <guid>https://dev.to/prabhu_ponnambalam_67867a/the-deadlock-detective-gdb-techniques-for-solving-thread-crimes-in-real-time-il0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Ever had your production application freeze mysteriously? No crashes, no errors—just complete silence? You're likely dealing with a deadlock.                                                &lt;/p&gt;

&lt;p&gt;Unlike crashes that leave evidence (core dumps, stack traces), deadlocks are silent killers. Your process is alive but completely unresponsive, with threads locked in an eternal embrace.   &lt;/p&gt;

&lt;p&gt;Today, I'll show you how to use GDB to perform a live autopsy on a deadlocked application and identify the exact lines of code responsible.                                                  &lt;/p&gt;

&lt;p&gt;💡 Pro Tip: This tutorial assumes basic familiarity with C++ and multithreading. If you're new to threads, check out &lt;a href="https://en.cppreference.com/w/cpp/thread" rel="noopener noreferrer"&gt;https://en.cppreference.com/w/cpp/thread&lt;/a&gt;.                               &lt;/p&gt;




&lt;h2&gt;
  
  
  Deadlock issue
&lt;/h2&gt;

&lt;p&gt;Execute the application in one terminal:                                                                                                                                                         &lt;/p&gt;

&lt;p&gt;./prabhu_application.out                                                                                                                                                                            &lt;/p&gt;

&lt;p&gt;You'll see this output, then the application will freeze:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prabhu ~/pgms $ g++ application.cpp -o prabhu_application.out
Prabhu ~/pgms $ ./prabhu_application.out
Creating two threads that will deadlock...                                                                                                                                                   
Thread 1: locks mutex1 -&amp;gt; mutex2                                                                                                                                                             
Thread 2: locks mutex2 -&amp;gt; mutex1                                                                                                                                                             
===============================                                                                                                                                                                                                                                                                                                                                                          
Main: Waiting for threads to complete...                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
Thread 1: Attempting to lock mutex1...                                                                                                                                                       
Thread 1: Locked mutex1                                                                                                                                                                      
Thread 2: Attempting to lock mutex2...                                                                                                                                                       
Thread 2: Locked mutex2                                                                                                                                                                      
Thread 1: Attempting to lock mutex2...                                                                                                                                                       
Thread 2: Attempting to lock mutex1...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;← HANGS HERE FOREVER&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Notice the last two lines—both threads are trying to acquire their second lock. Neither will succeed. The process is now deadlocked. Time to investigate! 🕵️                                 &lt;/p&gt;


&lt;h2&gt;
  
  
  The Investigation Begins: Attaching GDB
&lt;/h2&gt;

&lt;p&gt;Open a second terminal while leaving the frozen process running.                                                                                                                             &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find the process ID:&lt;/strong&gt;                                                                                                                                                                       &lt;/p&gt;

&lt;p&gt;pgrep -a application                                                                                                                                                                      &lt;/p&gt;

&lt;p&gt;This returns something like:                                                                                                                                                                 &lt;/p&gt;

&lt;p&gt;12345 ./application&lt;br&gt;&lt;br&gt;
                                                                                                                                                                                                                                                                                                                                                The number 12345 is your Process ID (PID).                                                                                                                                                   &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternative method:&lt;/strong&gt;&lt;br&gt;
You can also use ps aux | grep application | grep -v grep                                                                                                              &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attach GDB to the running process:&lt;/strong&gt;                                                                                                                                                           &lt;/p&gt;

&lt;p&gt;sudo gdb -p 12345                                                                                                                                                                            &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GDB will display something like:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attaching to process 12345                                                                                                                                                                   
[New LWP 12346]                                                                                                                                                                              
[New LWP 12347]                                                                                                                                                                              
[Thread debugging using libthread_db enabled]                                                                                                                                                
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".                                                                                                                   
0x00007f8a1bef2f3d in __GI___libc_read () from /lib/x86_64-linux-gnu/libc.so.6                                                                                                               
(gdb)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The (gdb) prompt means we're in control. Let's investigate!                                                                                                                                  &lt;/p&gt;




&lt;h2&gt;
  
  
  Finding the Deadlock: Thread Analysis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Quick Thread Overview:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  (gdb) info threads                                                                                                                                                                           

  Output:                                                                                                                                                                                      

    Id   Target Id                                    Frame                                                                                                                                    
  * 1    Thread 0x7f8a1c2a3740 (LWP 12345) "deadlock" 0x00007f8a1bef2f3d in __GI___libc_read (...)                                                                                             
    2    Thread 0x7f8a1ba92700 (LWP 12346) "deadlock" 0x00007f8a1bee3a4e in __lll_lock_wait ()                                                                                                 
    3    Thread 0x7f8a1b291700 (LWP 12347) "deadlock" 0x00007f8a1bee3a4e in __lll_lock_wait ()                                                                                                 

  🚨 Threads 2 and 3 are both in __lll_lock_wait() - this is the low-level mutex wait. When multiple threads are here, you likely have a deadlock.  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Investigating Thread 2
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Switch to Thread 2 and get its backtrace:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  (gdb) thread 2                                                                                                                                                                               
  (gdb) bt                                                                                                                                                                                     

  Output:                                                                                                                                                                                      

  #0  0x00007f8a1bee3a4e in __lll_lock_wait ()                                                                                                                                                 
  #1  0x00007f8a1bedc2a3 in __GI___pthread_mutex_lock (mutex=0x555555558060 &amp;lt;mutex2&amp;gt;)                                                                                                          
  #2  0x00005555555554b2 in __gthread_mutex_lock (__mutex=0x555555558060 &amp;lt;mutex2&amp;gt;)                                                                                                             
  #3  0x0000555555555687 in std::mutex::lock (this=0x555555558060 &amp;lt;mutex2&amp;gt;)                                                                                                                    
  #4  0x00005555555552f1 in thread1_function () at application.cpp:21                                                                                                                        
  #5  0x0000555555555a7e in std::__invoke_impl&amp;lt;void, void (*)()&amp;gt; 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Critical Information
&lt;/h2&gt;

&lt;p&gt;Frame #4 shows our code: thread1_function() at line 21                                                                                                                                       &lt;/p&gt;

&lt;p&gt;Frame #1 shows it's waiting for mutex2 at address 0x555555558060                                                                                                                             &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jump to the source:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  (gdb) frame 4                                                                                                                                                                                
  (gdb) list                                                                                                                                                                                   

  Output:                                                                                                                                                                                      

  16      std::cout &amp;lt;&amp;lt; "Thread 1: Locked mutex1" &amp;lt;&amp;lt; std::endl;                                                                                                                                                                                                                                                                                                                     
  18      std::this_thread::sleep_for(std::chrono::milliseconds(100));                                                                                                                         
  19                                                                                                                                                                                           
  20      std::cout &amp;lt;&amp;lt; "Thread 1: Attempting to lock mutex2..." &amp;lt;&amp;lt; std::endl;                                                                                                                  
  21      mutex2.lock();  ← 🔴 STUCK HERE                                                                                                                                                      
  22      std::cout &amp;lt;&amp;lt; "Thread 1: Locked mutex2" &amp;lt;&amp;lt; std::endl;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Look at what it already holds:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  (gdb) list 10,20                                                                                                                                                                             

  14      mutex1.lock();  ← ✅ THIS SUCCEEDED                                                                                                                                                  
  15      std::cout &amp;lt;&amp;lt; "Thread 1: Locked mutex1" &amp;lt;&amp;lt; std::endl;                                                                                                                                                                                                                                                                                                                          
  21      mutex2.lock();  ← 🔴 BLOCKED HERE  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Thread 2 Summary:&lt;/strong&gt;                                                                                                                                                                             &lt;/p&gt;

&lt;p&gt;Holds: mutex1 (line 14)&lt;br&gt;&lt;br&gt;
  Waiting for: mutex2 (line 21)&lt;br&gt;&lt;br&gt;
  Blocked at: application.cpp:21                                                                                                                                                             &lt;/p&gt;


&lt;h2&gt;
  
  
  Investigating Thread 3
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Switch to Thread 3:&lt;/strong&gt;                                                                                                                                                                          &lt;/p&gt;

&lt;p&gt;(gdb) thread 3&lt;br&gt;&lt;br&gt;
  (gdb) bt                                                                                                                                                                                     &lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  #0  0x00007f8a1bee3a4e in __lll_lock_wait ()                                                                                                                                                 
  #1  0x00007f8a1bedc2a3 in __GI___pthread_mutex_lock (mutex=0x555555558040 &amp;lt;mutex1&amp;gt;)                                                                                                          
  #2  0x00005555555554b2 in __gthread_mutex_lock (__mutex=0x555555558040 &amp;lt;mutex1&amp;gt;)                                                                                                             
  #3  0x0000555555555687 in std::mutex::lock (this=0x555555558040 &amp;lt;mutex1&amp;gt;)                                                                                                                    
  #4  0x000055555555535f in thread2_function () at application.cpp:44

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frame #4 shows: thread2_function() at line 44                                                                                                                                                &lt;/p&gt;

&lt;p&gt;Frame #1 shows it's waiting for mutex1 at address 0x555555558040                                                                                                                             &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check the source:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  (gdb) frame 4                                                                                                                                                                                
  (gdb) list                                                                                                                                                                                   

  Output:                                                                                                                                                                                      

  39      std::cout &amp;lt;&amp;lt; "Thread 2: Locked mutex2" &amp;lt;&amp;lt; std::endl;                                                                                                                                 
  40                                                                                                                                                                                           
  41      std::this_thread::sleep_for(std::chrono::milliseconds(100));                                                                                                                         
  42                                                                                                                                                                                           
  43      std::cout &amp;lt;&amp;lt; "Thread 2: Attempting to lock mutex1..." &amp;lt;&amp;lt; std::endl;                                                                                                                  
  44      mutex1.lock();  ← 🔴 STUCK HERE                                                                                                                                                      
  45      std::cout &amp;lt;&amp;lt; "Thread 2: Locked mutex1" &amp;lt;&amp;lt; std::endl;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it already holds:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(gdb) list 33,44                                                                                                                                                                             

  37      mutex2.lock();  ← ✅ THIS SUCCEEDED                                                                                                                                                  
  38      std::cout &amp;lt;&amp;lt; "Thread 2: Locked mutex2" &amp;lt;&amp;lt; std::endl;                                                                                                                                 
  ...                                                                                                                                                                                          
  44      mutex1.lock();  ← 🔴 BLOCKED HERE

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Thread 3 Summary:&lt;/strong&gt;                                                                                                                                                                             &lt;/p&gt;

&lt;p&gt;Holds: mutex2 (line 37)&lt;br&gt;&lt;br&gt;
  Waiting for: mutex1 (line 44)&lt;br&gt;&lt;br&gt;
  Blocked at: application.cpp:44                                                                                                                                            &lt;/p&gt;


&lt;h2&gt;
  
  
  The Smoking Gun: Circular Dependency
&lt;/h2&gt;

&lt;p&gt;Let's lay out all the evidence we've collected:                                                                                                                                              &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thread 1's Situation:&lt;/strong&gt;                                                                                                                                                                       &lt;/p&gt;

&lt;p&gt;Location: application.cpp:21&lt;br&gt;&lt;br&gt;
  Status: ✅ Successfully holds mutex1&lt;br&gt;&lt;br&gt;
  Problem: ❌ Waiting for mutex2 (which Thread 2 has locked)&lt;br&gt;&lt;br&gt;
  Can't proceed until: Thread 2 releases mutex2                                                                                                                                                &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thread 2's Situation:&lt;/strong&gt;                                                                                                                                                                         &lt;/p&gt;

&lt;p&gt;Location: application.cpp:44&lt;br&gt;&lt;br&gt;
  Status: ✅ Successfully holds mutex2&lt;br&gt;&lt;br&gt;
  Problem: ❌ Waiting for mutex1 (which Thread 1 has locked)&lt;br&gt;&lt;br&gt;
  Can't proceed until: Thread 1 releases mutex1                                                                                                                                                &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Deadlock Cycle:&lt;/strong&gt;                                                                                                                                                                           &lt;/p&gt;

&lt;p&gt;Thread 1 needs mutex2 (held by Thread 2)&lt;br&gt;&lt;br&gt;
      ↓&lt;br&gt;&lt;br&gt;
  Thread 2 needs mutex1 (held by Thread 1)&lt;br&gt;&lt;br&gt;
      ↓&lt;br&gt;&lt;br&gt;
  Thread 1 needs mutex2 (held by Thread 2)&lt;br&gt;&lt;br&gt;
      ↓&lt;br&gt;&lt;br&gt;
  &lt;strong&gt;[Infinite loop - DEADLOCK!]&lt;/strong&gt;                                                                                                                                                                  &lt;/p&gt;

&lt;p&gt;Or &lt;strong&gt;visualized as a circle:&lt;/strong&gt;                                                                                                                                                                   &lt;/p&gt;

&lt;p&gt;Thread 1 → waiting for → mutex2&lt;br&gt;&lt;br&gt;
                             ↓&lt;br&gt;&lt;br&gt;
                        held by&lt;br&gt;&lt;br&gt;
                             ↓&lt;br&gt;&lt;br&gt;
                          Thread 2 → waiting for → mutex1&lt;br&gt;&lt;br&gt;
                                                     ↓&lt;br&gt;&lt;br&gt;
                                                 held by&lt;br&gt;&lt;br&gt;
                                                     ↓&lt;br&gt;&lt;br&gt;
                                                Thread 1&lt;br&gt;&lt;br&gt;
                                             [cycle complete!]                                                                                                                                 &lt;/p&gt;


&lt;h2&gt;
  
  
  Prevention: Never Let This Happen Again
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Solution 1: Global Lock Ordering&lt;/strong&gt;                                                                                                                                                           &lt;/p&gt;

&lt;p&gt;The rule: Always acquire locks in the same order across all threads.                                                                                                                         &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Both threads now use the same order                                                                                                                                                       
void thread1_function()                                                                                                                                                                      
{                                                                                                                                                                                            
  mutex1.lock();  // First                                                                                                                                                                   
  mutex2.lock();  // Second                                                                                                                                                                  
  // ... critical section ...                                                                                                                                                                
  mutex2.unlock();                                                                                                                                                                           
  mutex1.unlock();                                                                                                                                                                           
}                                                                                                                                                                                            

void thread2_function()                                                                                                                                                                      
{                                                                                                                                                                                            
   mutex1.lock();  // First (SAME ORDER AS THREAD 1)                                                                                                                                          
   mutex2.lock();  // Second (SAME ORDER AS THREAD 1)                                                                                                                                                                                                                                                                                                                
   mutex2.unlock();
   mutex1.unlock();   

}  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; If everyone acquires resources in the same sequence, circular dependencies cannot form. No matter how threads interleave, one will successfully acquire both locks, complete &lt;br&gt;
  its work, release them, and then the other can proceed.                                                                                                                                      &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution 2: std::scoped_lock (Recommended for C++17+)&lt;/strong&gt;                                                                                                                                        &lt;/p&gt;

&lt;p&gt;The modern, foolproof approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   #include &amp;lt;mutex&amp;gt;                                                                                                                                                                             

   void thread1_function()                                                                                                                                                                      
   {                                                                                                                                                                                            
     std::scoped_lock lock(mutex1, mutex2);                                                                                                                                                     
     // Critical section                                                                                                                                                                        
     std::cout &amp;lt;&amp;lt; "Thread 1: In critical section" &amp;lt;&amp;lt; std::endl;                                                                                                                                 
     // Locks automatically released in correct order                                                                                                                                           
   }                                                                                                                                                                                            


   void thread2_function()                                                                                                                                                                     
   {                                                                                                                                                                                            
     std::scoped_lock lock(mutex1, mutex2);  // Same call - always safe!                                                                                                                        
     // Critical section                                                                                                                                                                        
     std::cout &amp;lt;&amp;lt; "Thread 2: In critical section" &amp;lt;&amp;lt; std::endl;                                                                                                                                 
     // Locks automatically released in correct order                                                                                                                                           
   }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How std::scoped_lock works its magic:&lt;/strong&gt;                                                                                                                                                       &lt;/p&gt;

&lt;p&gt;Step 1: Takes multiple mutexes as arguments&lt;br&gt;&lt;br&gt;
  Step 2: Internally sorts them by memory address&lt;br&gt;&lt;br&gt;
  Step 3: Always acquires them in address order (consistent across all threads)&lt;br&gt;&lt;br&gt;
  Step 4: Uses RAII to guarantee release in reverse order&lt;br&gt;&lt;br&gt;
  Step 5: Exception-safe—releases locks even if your code throws                                                                                                                               &lt;/p&gt;

&lt;p&gt;Even if you pass the locks in different orders (scoped_lock(mutex2, mutex1) vs scoped_lock(mutex1, mutex2)), it normalizes them internally. Deadlock impossible!                             &lt;/p&gt;




&lt;h2&gt;
  
  
  Core Commands for Deadlock Hunting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Attach to a running process:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  gdb -p                                                                                                                                                                                 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;List all threads:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  info threads                                                                                                                                                                                &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Switch to a specific thread:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  thread 2                                                                                                                                                                                     &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get backtrace (call stack):&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  bt                                                                                                                                                                                           &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get backtrace with local variables:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  bt full                                                                                                                                                                                      &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get ALL thread backtraces (best for deadlock):&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  thread apply all bt                                                                                                                                                                          &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jump to a specific stack frame:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  frame 4                                                                                                                                                                                      &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Navigate stack frames:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  up      # Move toward caller&lt;br&gt;&lt;br&gt;
  down    # Move toward callee                                                                                                                                                                 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Show source code:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  list&lt;br&gt;&lt;br&gt;
  list 10,20    # Show lines 10-20                                                                                                                                                             &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspect variables:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  print variable_name&lt;br&gt;&lt;br&gt;
  print mutex1&lt;br&gt;&lt;br&gt;
  info locals&lt;br&gt;&lt;br&gt;
  info args                                                                                                                                                                                    &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch for changes:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  watch variable_name                                                                                                                                                                          &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Detach without killing process:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
  detach&lt;br&gt;&lt;br&gt;
  quit                                                                                                                                                                                         &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick Deadlock Check (One Command)&lt;/strong&gt;                                                                                                                                                         &lt;/p&gt;

&lt;p&gt;thread apply all bt | grep -A 5 "lll_lock_wait"                                                                                                                                              &lt;/p&gt;

&lt;p&gt;This shows all threads stuck in lock waits with context.                                                                                                                                     &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generate GDB report&lt;/strong&gt;                                                                                                                                                                   &lt;/p&gt;

&lt;p&gt;gdb -p PID -batch -ex 'thread apply all bt' -ex detach &amp;gt; gdb_bt.txt                                                                                                                                                                    &lt;/p&gt;

&lt;p&gt;This generates a complete report automatically without manual intervention—perfect for production debugging!                                                                                 &lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've completed a full deadlock investigation:                                                                                                                                               &lt;/p&gt;

&lt;p&gt;✅ Created a deadlock scenario&lt;br&gt;&lt;br&gt;
  ✅ Attached GDB to the frozen process&lt;br&gt;&lt;br&gt;
  ✅ Identified threads in wait states&lt;br&gt;&lt;br&gt;
  ✅ Analyzed backtraces to find lock locations&lt;br&gt;&lt;br&gt;
  ✅ Discovered the circular dependency&lt;br&gt;&lt;br&gt;
  ✅ Identified the root cause (lock ordering violation)&lt;br&gt;&lt;br&gt;
  ✅ Implemented multiple prevention strategies                                                                                                                                                &lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;GDB Documentation: &lt;a href="https://sourceware.org/gdb/documentation/" rel="noopener noreferrer"&gt;https://sourceware.org/gdb/documentation/&lt;/a&gt;                                                                                                                                 &lt;/p&gt;

&lt;p&gt;C++ Concurrency in Action by Anthony Williams - The definitive guide to multithreading in C++                                                                                                &lt;/p&gt;

&lt;p&gt;ThreadSanitizer Manual: &lt;a href="https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual" rel="noopener noreferrer"&gt;https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual&lt;/a&gt;                                                                                                   &lt;/p&gt;

&lt;p&gt;CPP Reference - Thread Support: &lt;a href="https://en.cppreference.com/w/cpp/thread" rel="noopener noreferrer"&gt;https://en.cppreference.com/w/cpp/thread&lt;/a&gt;                                          &lt;/p&gt;

</description>
      <category>cli</category>
      <category>linux</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
