Apa itu spinlock?

Spinlock membuat thread menunggu secara aktif (busy-wait) dalam sebuah loop sampai lock tersedia, alih-alih tidur seperti mutex. Selama menunggu, thread terus-menerus mengecek apakah lock sudah bisa diambil dan tidak pernah benar-benar berhenti memakai CPU.

Mutex sebaliknya: menidurkan thread saat menunggu, lalu membangunkannya lagi saat lock dilepas.

Ilustrasi

Mutex: Thread2 gagal lock() → BLOCKED, tidur, CPU bebas → dibangunkan scheduler saat lock dilepas
 
Spinlock: Thread2 spin_lock() → cek... cek... cek... (CPU sibuk) → dapat begitu lock dilepas

Kenapa kadang lebih baik

Mutex punya overhead: saat thread gagal dapat lock, kernel harus menidurkannya (context switch), lalu membangunkannya lagi nanti (context switch lagi). Ini tidak gratis.

Kalau critical section-nya sangat pendek — misal cuma increment satu variabel — waktu context switch ke sleep dan balik lagi bisa lebih lama dari waktu tunggu itu sendiri. Di sinilah spinlock, yang tidak perlu context switch sama sekali, bisa lebih cepat. Tapi ini cuma masuk akal di kondisi tertentu: multi-core (supaya pemegang lock bisa jalan paralel di core lain) dan critical section super pendek.

Implementasi

Struktur proyek

.
└── spinlock_demo.c

Step 1: program dengan spinlock

Dua thread increment counter yang sama sebanyak 1 juta kali.

#include <pthread.h>
#include <stdio.h>
 
pthread_spinlock_t spinlock;
long counter = 0;
 
void *increment(void *arg)
{
    for (int i = 0; i < 1000000; i++) {
        pthread_spin_lock(&spinlock);
        counter++;
        pthread_spin_unlock(&spinlock);
    }
    return NULL;
}
 
int main(void)
{
    pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
 
    pthread_t t1, t2;
    pthread_create(&t1, NULL, increment, NULL);
    pthread_create(&t2, NULL, increment, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
 
    printf("Counter akhir: %ld (expected: 2000000)\n", counter);
 
    pthread_spin_destroy(&spinlock);
    return 0;
}

Step 2: versi mutex untuk perbandingan

#include <pthread.h>
#include <stdio.h>
 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
long counter = 0;
 
void *increment(void *arg)
{
    for (int i = 0; i < 1000000; i++) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
 
int main(void)
{
    pthread_t t1, t2;
    pthread_create(&t1, NULL, increment, NULL);
    pthread_create(&t2, NULL, increment, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
 
    printf("Counter akhir: %ld (expected: 2000000)\n", counter);
    return 0;
}

Penjelasan

pthread_spin_lock loop terus-menerus mengecek lock sampai berhasil didapat — tidak pernah menidurkan thread, CPU tetap sibuk selama menunggu. pthread_spin_unlock melepas lock, thread lain yang sedang spin akan mendeteksinya di iterasi berikutnya.

Untuk membandingkan waktu eksekusi kedua versi, tambahkan timing sederhana di main:

#include <time.h>
 
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// ... buat & join thread ...
clock_gettime(CLOCK_MONOTONIC, &end);
 
double elapsed = (end.tv_sec - start.tv_sec) +
                  (end.tv_nsec - start.tv_nsec) / 1e9;
printf("Waktu: %.4f detik\n", elapsed);

Jalankan versi spinlock dan mutex dengan timing ini untuk melihat perbedaan nyata di mesin kamu — hasilnya bisa beda tergantung jumlah core dan beban sistem saat itu.

Kapan spinlock menang

Skenario Spinlock Mutex
Critical section sangat pendek, multi-core, low contention lebih cepat overhead context switch terasa
Critical section panjang (ada I/O, sleep, dsb.) buang CPU sia-sia lebih efisien
Single-core hampir selalu lebih buruk lebih baik
Contention tinggi CPU makin panas, tidak fair lebih stabil

Kompilasi

gcc -o spinlock_demo spinlock_demo.c -lpthread
./spinlock_demo

Best practices

Jangan pernah spin di atas operasi yang bisa blocking — I/O, syscall, atau apapun yang berpotensi lama, karena thread lain akan buang CPU cycle tanpa progres. Spinlock cuma masuk akal untuk critical section super pendek, beberapa instruksi CPU, bukan operasi kompleks. Di single-core, spinlock hampir selalu kalah dari mutex, karena thread yang spin tidak memberi kesempatan pemegang lock untuk jalan.

Spinlock juga tidak fair — tidak ada jaminan FIFO, thread bisa starvation kalau contention tinggi dan terus-menerus. Jadi selalu benchmark, jangan asumsi — ukur langsung di mesin dan beban kerja yang relevan.

Kesimpulan

Spinlock itu trade-off: busy-wait yang boros CPU, ditukar dengan menghindari overhead context switch. Cocok untuk critical section yang sangat pendek di sistem multi-core dengan contention rendah, tapi bukan pengganti mutex secara umum — ini alat khusus untuk kasus yang spesifik1.

Footnotes

  1. Beberapa implementasi mutex modern, termasuk glibc, sebenarnya sudah menggabungkan strategi ini secara internal — spin sebentar dulu sebelum benar-benar tidur (adaptive mutex) — jadi di banyak kasus, mutex bawaan sistem sudah cukup pintar tanpa perlu spinlock manual. ↩