Skip to main content

Command Palette

Search for a command to run...

Mutex vs Spinlock

Updated
5 min read
Mutex vs Spinlock

Introduction

Concurrency is everywhere inside the Linux kernel. Multiple threads, processes, softirqs, tasklets, and interrupt handlers can access the same shared data simultaneously.

Without synchronization, this leads to:

  • Race conditions

  • Data corruption

  • Unpredictable behavior

  • Difficult-to-debug kernel crashes

Two of the most common synchronization primitives used by kernel developers are:

  • Mutex

  • Spinlock

Although both protect critical sections, they work very differently internally and are designed for different situations.


The Locking Problem

Imagine three kernel threads attempting to update the same shared counter:

shared_counter++;

If all threads execute this simultaneously, the result becomes unpredictable.

To avoid this, we protect the critical section:

lock();

shared_counter++;

unlock();

Only one thread can enter the protected region at a time.

The important question is:

What happens to the threads that fail to acquire the lock?

The answer depends on whether we're using a mutex or a spinlock.


How a Mutex Works

A mutex allows only one owner.

When a thread attempts to acquire a locked mutex:

  1. It cannot proceed.

  2. It is put to sleep.

  3. The scheduler removes it from the CPU.

  4. It waits until the mutex becomes available.

Example:

mutex_lock(&my_mutex);

/* critical section */

mutex_unlock(&my_mutex);

Characteristics

  • Blocking lock

  • Sleeping is allowed

  • Context switch occurs

  • Suitable for long critical sections


How a Spinlock Works

A spinlock behaves differently.

If a thread cannot acquire the lock:

  • It does not sleep

  • It repeatedly checks whether the lock is available

  • It continuously waits ("spins")

Example:

spin_lock(&my_lock);

/* critical section */

spin_unlock(&my_lock);

Conceptually:

while (lock_is_busy)
    ;

Real Linux implementations are much more optimized than this simple representation.


Why Spinning Can Be Faster

Putting a thread to sleep is expensive.

The kernel must:

  1. Invoke the scheduler

  2. Save thread state

  3. Perform a context switch

  4. Wake the thread later

  5. Perform another context switch

A mutex therefore incurs at least:

Sleep Context Switch
+
Wakeup Context Switch

For very short critical sections, this overhead can exceed the actual work being protected.


The Performance Rule

Let:

  • t_locked = time spent in critical section

  • t_ctxsw = context switch time

If:

t_locked < 2 × t_ctxsw

then using a mutex becomes inefficient.

The system spends more time managing threads than performing useful work.

This phenomenon is known as thrashing.

In such cases, a spinlock is often the better choice.


Mutex vs Spinlock Comparison

Feature Mutex Spinlock
Waiting strategy Sleep Spin
Context switch Yes No
Can block Yes No
Can sleep inside critical section Yes No
Interrupt-safe No Yes
Overhead Higher Lower
Best for Long critical sections Short critical sections

Atomic Context Changes Everything

The Linux kernel has a very important rule:

Code running in atomic context cannot sleep.

Examples include:

  • Interrupt handlers

  • Softirqs

  • Tasklets

  • Other atomic execution paths

Since mutexes sleep internally, they cannot be used here.

Incorrect:

irq_handler()
{
    mutex_lock(&lock);   /* WRONG */
}

Correct:

irq_handler()
{
    spin_lock(&lock);

    /* critical section */

    spin_unlock(&lock);
}

Sleeping Inside a Spinlock Is Forbidden

This is a common beginner mistake.

Never do:

spin_lock(&lock);

msleep(100);

spin_unlock(&lock);

Or:

spin_lock(&lock);

wait_event(...);

spin_unlock(&lock);

A spinlock assumes the holder will release it quickly.

Sleeping while holding a spinlock can freeze the system or trigger kernel warnings.


Practical Decision Guide

Use a Spinlock When

  • Running in interrupt context

  • Running in atomic context

  • Critical section is extremely short

  • Sleeping is not allowed

  • Maximum performance is required

Example:

spin_lock(&lock);
counter++;
spin_unlock(&lock);

Use a Mutex When

  • Running in process context

  • Blocking I/O may occur

  • Sleeping may occur

  • Critical section is relatively long

Example:

mutex_lock(&lock);

copy_to_user(...);

mutex_unlock(&lock);

Determining the Current Context

Linux provides helpers to determine where code is executing.

Example:

if (in_task())
{
    /* process context */
}
else
{
    /* atomic or interrupt context */
}

General rule:

  • Process context → mutex or spinlock

  • Interrupt/atomic context → spinlock only


Mental Model

Think of the locks this way:

Mutex

"I can't get the lock. I'll sleep until someone wakes me."

Spinlock

"I can't get the lock. I'll keep checking until it becomes available."

That single difference determines almost every practical use case.


Key Takeaways

  • Both mutexes and spinlocks protect critical sections.

  • Mutex losers sleep and later wake up.

  • Spinlock losers remain active and wait.

  • Mutexes have higher overhead because of context switching.

  • Spinlocks are ideal for short, non-blocking operations.

  • Mutexes are ideal for longer operations that may sleep.

  • Never use a mutex in interrupt or atomic context.

  • Never sleep while holding a spinlock.

Choosing the correct lock is one of the most important design decisions in Linux kernel development. Understanding when threads sleep and when they spin is the foundation of writing safe kernel code.