vsync / spinlock / mcslock.h

Mellor-Crummey Scott Lock - the well-known scalable lock.

Groups: Fair locks

MCS lock is a scalable lock that guarantees FIFO order. The MCS lock forms a queue of waiting threads (each thread has one node in the queue), and lets them spin on local variables of their nodes.

Note that this implementation is not reentrant.

Example:

/*
 * The following example shows how to use the MCS lock. Each threads needs a
 * context, i.e., an instance of `mcs_node_t`. When acquiring or releasing the
 * lock, one needs to pass the lock and the context as arguments. The context
 * of a thread should not be used in multiple MCS locks at the same time.
 */
#include <vsync/spinlock/mcslock.h>
#include <vsync/common/assert.h>
#include <pthread.h>
#include <stdio.h>

#define N            12
#define MAX_THREADS  N
#define EXPECTED_VAL N

mcslock_t g_lock = MCSLOCK_INIT();
mcs_node_t g_msc_nodes[MAX_THREADS];

vuint32_t g_x = 0;
vuint32_t g_y = 0;

void *
run(void *args)
{
    vsize_t tid      = (vsize_t)args;
    mcs_node_t *node = &g_msc_nodes[tid];

    mcslock_acquire(&g_lock, node);
    g_x++;
    g_y++;
    mcslock_release(&g_lock, node);

    (void)args;
    return NULL;
}

int
main(void)
{
    pthread_t threads[N];

    for (vsize_t i = 0; i < N; i++) {
        pthread_create(&threads[i], NULL, run, (void *)i);
    }

    for (vsize_t i = 0; i < N; i++) {
        pthread_join(threads[i], NULL);
    }

    ASSERT(g_x == EXPECTED_VAL);
    ASSERT(g_x == g_y);
    printf("Final value %u\n", g_x);
    return 0;
}

References:

J.Mellor-Crummey, M.L.Scott - [Algorithms for scalable synchronization on shared-memory multiprocessors. ACM Trans. on Computer Systems. 1991]


Macros

Macro Description
MCSLOCK_INIT Initializer of mcslock_t.

Macro MCSLOCK_INIT

MCSLOCK_INIT()

Initializer of mcslock_t.


Functions

Function Description
mcslock_init Initializes the MCS lock.
mcslock_tryacquire Tries to acquire the MCS lock.
mcslock_acquire Acquires the MCS lock.
mcslock_release Releases the MCS lock.
mcslock_has_waiters Returns whether there is a thread waiting to acquire the MCS lock.

Function mcslock_init

static void mcslock_init(mcslock_t *l)

Initializes the MCS lock.

Parameters:

  • l: address of mcslock_t object.

Note: alternatively use MCSLOCK_INIT.

Function mcslock_tryacquire

static vbool_t mcslock_tryacquire(mcslock_t *l, mcs_node_t *node)

Tries to acquire the MCS lock.

Parameters:

  • l: address of mcslock_t object.
  • node: address of mcs_node_t object, associated with the calling thread/core.

Returns: true, on success.

Returns: false, on fail.

Function mcslock_acquire

static void mcslock_acquire(mcslock_t *l, mcs_node_t *node)

Acquires the MCS lock.

Parameters:

  • l: address of mcslock_t object.
  • node: address of mcs_node_t object, associated with the calling thread/core.

Function mcslock_release

static void mcslock_release(mcslock_t *l, mcs_node_t *node)

Releases the MCS lock.

Parameters:

  • l: address of mcslock_t object.
  • node: address of mcs_node_t object, associated with the calling thread/core.

Function mcslock_has_waiters

static vbool_t mcslock_has_waiters(mcslock_t *l, mcs_node_t *node)

Returns whether there is a thread waiting to acquire the MCS lock.

This function should only be called by the current owner of the lock.

Parameters:

  • l: address of mcslock_t object.
  • node: address of mcs_node_t object, associated with the calling thread/core.

Returns: true, if threads are waiting to acquire the lock.

Returns: false, otherwise.