Skip to content

Completion Synchronization

Overview

Completion is a Linux kernel synchronization primitive designed for one-shot event notification.

It allows one execution context to wait until another execution context signals that an operation has completed.

Common use cases include:

  • Hardware initialization
  • Firmware loading
  • DMA completion
  • Worker startup synchronization
  • Probe synchronization

Completion provides a simpler alternative to wait queues when only a single completion event must be tracked.


Why Completion Exists

Without completion, the same behavior is often implemented using:

bool done;

wait_event(wq, done);

done = true;
wake_up(&wq);

Completion encapsulates this pattern into a dedicated synchronization primitive.

Instead of expressing:

Wait until a condition becomes true.

Completion expresses:

Wait until an operation finishes.


Completion Lifecycle

Typical workflow:

init_completion()
wait_for_completion()
complete()

The waiter blocks until another execution context signals completion.


Completion Object

Kernel definition:

struct completion {
    unsigned int done;
    wait_queue_head_t wait;
};

The internal state contains:

  • Completion counter
  • Wait queue

Completion is built on top of Linux wait queues.


Initialization

Initialize a completion object:

init_completion(&done);

The completion state becomes:

not completed

Waiting for Completion

Wait indefinitely:

wait_for_completion(&done);

The caller sleeps until completion is signaled.


Timeout Version

Wait with timeout:

wait_for_completion_timeout(
        &done,
        timeout);

Return value:

Return Meaning
0 Timeout
> 0 Completed

Signaling Completion

Signal a single waiter:

complete(&done);

Signal all waiters:

complete_all(&done);

complete() vs complete_all()

complete()

Wake a single waiter.

Example:

WAITER-1
WAITER-2
WAITER-3

complete()

Result:

WAITER-1 wakes
WAITER-2 remains blocked
WAITER-3 remains blocked

complete_all()

Wake all waiters.

Example:

WAITER-1
WAITER-2
WAITER-3

complete_all()

Result:

WAITER-1 wakes
WAITER-2 wakes
WAITER-3 wakes

Completion State Persistence

Completion remains in the completed state after:

complete()

or

complete_all()

Subsequent waiters return immediately.

Example:

complete()

wait_for_completion()

returns immediately.


Reinitialization

Reset completion state:

reinit_completion(&done);

After reinitialization:

wait_for_completion()

blocks again until another completion event occurs.


Completion vs Wait Queue

Feature Completion Wait Queue
One-shot event Yes Possible
Repeated events No Yes
Multiple conditions No Yes
State machine support No Yes
Initialization synchronization Excellent Acceptable
Event-driven state machine Limited Excellent

Typical Use Cases

Completion

Recommended for:

  • Firmware ready
  • DMA complete
  • Hardware initialization
  • Worker startup synchronization
  • Probe synchronization

Example:

probe()
wait_for_completion()
hardware ready

Wait Queue

Recommended for:

  • Producer / consumer systems
  • State machines
  • Event loops
  • Driver event processing
  • Repeated notifications

Example:

wait_event(
        wq,
        state_changed);

RTOS Mapping

For developers with RTOS experience:

RTOS Concept Linux Kernel Primitive
Binary Event Completion
Event Group Wait Queue
Counting Semaphore Semaphore
Mutex Mutex

Completion most closely resembles a binary event used to signal that a specific operation has finished.


Common Mistakes

Forgetting Timeout Handling

Bad:

wait_for_completion()

A failed device may cause the caller to block forever.

Prefer:

wait_for_completion_timeout()

when waiting for hardware.


Assuming Timeout Means Worker Exit

Timeout only indicates:

completion did not occur

The worker may still be running.

Worker lifetime must be handled separately.


Forgetting reinit_completion()

After completion occurs:

wait_for_completion()

returns immediately.

Use:

reinit_completion()

before reusing the completion object.


Summary

Completion is a lightweight synchronization primitive for one-shot events.

Use completion when the goal is:

Wait until an operation finishes.

Use wait queues when the goal is:

Wait until a condition becomes true.