Linux System Programming · intermediate · ~10 min

Common mistakes with threads

Recognise the bugs that bite every C programmer learning pthreads.

Lesson

  1. Forgetting -pthread. Linker error or worse — code compiles but pthread_mutex calls become no-ops on some platforms.
  2. Passing &i from a loop. All threads see the post-loop value. Pass by value or by heap pointer.
  3. Race on a global counter. counter++ is not atomic. Use a mutex or _Atomic long.
  4. Holding a mutex across a blocking I/O call. Every other thread stalls. Drop the lock before I/O.
  5. Locks in different orders. Eventually two paths collide → deadlock. Adopt a global lock-ordering rule.
  6. Using signal() in a threaded program. Use sigaction() and consider blocking signals on worker threads with pthread_sigmask.
  7. if instead of while around cond_wait. Spurious wakeups break your logic.
  8. Returning a stack address from the thread function. Stack is gone the moment the thread exits.

Code examples

/* WRONG — race on shared counter */
static int counter = 0;
void *bump(void *_) { for (int i = 0; i < 1000000; i++) counter++; return NULL; }

/* RIGHT — atomic */
static _Atomic int counter = 0;
void *bump(void *_) { for (int i = 0; i < 1000000; i++) counter++; return NULL; }

Common mistakes

  • Believing TSan is overkill. Every multi-threaded C program should be exercised under -fsanitize=thread before shipping.

Summary

Eight recurring threading bugs. Use -pthread, pass args safely, lock around shared state, use while for cond_wait, never return a stack address.

Practice with these exercises