Site Tools


lock-convoy

Table of Contents

Lock convoy

Lock convoy is a pathological contention pattern where threads serialize behind a lock even though the lock is held only briefly. It occurs when a lock holder is preempted mid-critical-section, forcing all waiters to idle through the rest of that time slice before the holder even resumes. Once threads pile up waiting, the queue sustains itself because arrivals keep pace with drains.

Convoys are harder to diagnose than ordinary contention because the bottleneck is scheduling interaction around the lock, not the lock's actual hold time.

Example

This example simulates the convoy effect with preemption timing.

// compile: gcc -std=c11 -pthread -O2 -o convoy convoy.c
// run: ./convoy
// description: demonstrate lock convoy where queue self-sustains
 
#include <stdio.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <time.h>
 
atomic_flag lock = ATOMIC_FLAG_INIT;
int work_counter = 0;
 
void* worker(void* arg) {
    int id = *(int*)arg;
    for (int i = 0; i < 100; i++) {
        while (atomic_flag_test_and_set(&lock)) { }
 
        // Simulate critical section work
        work_counter++;
 
        atomic_flag_clear(&lock);
 
        // Simulate non-critical work
        usleep(10);
    }
    printf("Thread %d done\n", id);
    return NULL;
}
 
int main() {
    int num_threads = 4;
    pthread_t threads[num_threads];
    int ids[num_threads];
 
    clock_t start = clock();
 
    for (int i = 0; i < num_threads; i++) {
        ids[i] = i;
        pthread_create(&threads[i], NULL, worker, &ids[i]);
    }
 
    for (int i = 0; i < num_threads; i++) {
        pthread_join(threads[i], NULL);
    }
 
    clock_t end = clock();
    printf("Total work: %d, Time: %ld\n", work_counter, end - start);
 
    return 0;
}
lock-convoy.md · Last modified: by 127.0.0.1