Introduction

The spin lock is a low-level synchronization technique used in programming to protect shared resources (e.g., variables, data structures, memory segment etc.) from access by multiple threads at the same time. A spin lock is a lock that is implemented as a loop that continuously checks for the availability of the lock until it becomes available.

image 19
Spin Lock

In a spin lock, a thread that wants to acquire the lock repeatedly checks if the lock is available, by continuously spinning in a loop, rather than waiting for the lock to be released by another thread.

Spin locks are typically used where the critical section is expected to be very short, and the overhead for the lock is low. Spin locks are more efficient than other locking mechanisms, such as semaphores or mutexes, when the expected wait time is less than the time it takes to block and unblock a thread.

The main advantage of spin locks is their low overhead compared to other locking mechanisms. Spin locks do not require context switching or thread suspension, which makes them faster than other locks for short critical sections with low contention.

Spin locks can cause high CPU usage when there is high contention for the lock. In such cases, other locking mechanisms like mutexes or semaphores may be a better choice. Spin locks can also cause priority inversion, where a higher priority task is blocked by a lower priority task that is holding the lock.

So, we can say that spin locks and mutexes are suited for different purposes. Spin locks are preferred for short-term blocking scenarios, as they have lower overall overhead. Mutexes are preferred when threads are expected to be blocked for longer periods, as they have lower overall overhead during the blocking period.

Example

Suppose we have a shared counter variable that multiple threads can access and modify simultaneously. To make sure that the counter is accessed in a thread-safe manner, we can use a spin lock to synchronize access to the counter. Below is a sample code for this purpose on Linux environment:

#include <pthread.h>
#include <stdio.h>

// Declare a global counter variable which will be used by multiple threads
int counter = 0;

// Declare a spin lock
pthread_spinlock_t spinlock;

// Define a function to increment the counter
void* increment_counter(void* arg) {
    // Acquire the spin lock
    pthread_spin_lock(&spinlock);

    // Access and modify the shared counter
    counter++;

    // Release the spin lock
    pthread_spin_unlock(&spinlock);

    return NULL;
}

//Thread function
void* thread_function(void* arg) {
    while(1) {
        //Inclement the counter in thread safe manner
        increment_counter(NULL);
        
        sleep(1);
    };

    return NULL;
}


int main() {
    // Initialize the spin lock
    pthread_spin_init(&spinlock, 0);

    // Create multiple threads
    pthread_t threads[10];
    for (int i = 0; i < 10; i++) {
        pthread_create(&threads[i], NULL, thread_function, NULL);
    }

    // Wait for all threads to complete
    for (int i = 0; i < 10; i++) {
        pthread_join(threads[i], NULL);
    }

    // Destroy the spin lock
    pthread_spin_destroy(&spinlock);

    return 0;
}

Here, we have declared a global counter variable (counter) & a spin lock using the pthread_spinlock_t type. We have then initialized the spin lock using the pthread_spin_init function. pthread_spin_init is a defined in pthread.h.

The increment_counter() function is responsible for incrementing the counter variable in a thread-safe manner. To make it thread safe, we have acquired the spin lock using the pthread_spin_lock(), access and modify the counter variable, and then release the spin lock using the pthread_spin_unlock(). In the main function, we create 10 threads that call the increment_counter().

Advantages & Disadvantages

AdvantagesDis-advantages
Spin locks have low overhead for short-term blocking because they do not require the thread to block and switch contexts.Spin locks can lead to higher processor utilization because the waiting thread is continuously running in a loop, consuming CPU cycles while waiting for the lock to become available.
Spin locks are efficient for low contention scenarios because they avoid the overhead of thread blocking and context switching.Spin locks can cause priority inversion if a higher-priority thread is waiting on a spin lock held by a lower-priority thread.
Spin locks can provide fast and predictable locking for small critical sections of code.Spin locks can cause cache thrashing if multiple threads are repeatedly accessing the same cache line.
Spin locks can be implemented using atomic instructions on some platforms, which provides very fast locking and unlocking.Spin locks are not scalable and can cause contention and performance degradation in high contention scenarios.
Spin locks can be useful in real-time systems where predictable locking times are important.Spin locks do not support waiting on a lock with a timeout, which can cause deadlocks if a thread is waiting indefinitely for a lock that never becomes available.

References:

Related post: