Synchronization » CnxUniqueLock module

CnxUniqueLock provides scoped exclusive locking of any higher-level mutexes (eg CnxMutex, CnxSharedMutex, etc.) provided by Cnx. It allows for a simple, concise way to acquire the exclusive lock on a mutex and ensure that lock is released appropriately.

Example:

// MyThing.h
#include <Cnx/sync/Mutex.h>
static MyType my_very_important_thing;
static CnxMutex* my_thing_mutex;

void init_my_thing(void);
void update_my_thing(u64 value);
u64 get_value_from_my_thing(void);

// MyThing.c
#include <Cnx/Allocators.h>
#include <Cnx/sync/UniqueLock.h>
#include "MyThing.h"
void init_my_thing(void) {
    if(my_thing_mutex == nullptr) {
        my_thing_mutex = cnx_allocator_allocate_t(CnxMutex, DEFAULT_ALLOCATOR);
        *my_thing_mutex = cnx_mutex_new();

        {
            // use a `CnxUniqueLock` to lock `my_thing_mutex`.
            // mark `lock` as `maybe_unused` so we don't get warnings about an unused variable
            __attr(maybe_unused) UniqueLock lock = cnx_unique_lock(*my_thing_mutex);
            my_very_important_thing = {
                // important intialization
            };
            // `lock` automatically destroyed at scope end, releasing the lock on
`my_thing_mutex`
        }
    }
}

void update_my_thing(u64 value) {
    __attr(maybe_unused) UniqueLock lock = cnx_unique_lock(*my_thing_mutex);
    my_very_important_thing.value = value;
    // `lock` automatically destroyed at scope end, releasing the lock on `my_thing_mutex`
}

u64 get_value_from_my_thing(void) {
    __attr(maybe_unused) UniqueLock lock = cnx_unique_lock(*my_thing_mutex);
    let val = my_very_important_thing.value;
    return val;
    // `lock` automatically destroyed at scope end, releasing the lock on `my_thing_mutex`
}


// do some compute intensive task...
// update the value
update_my_thing(new_value);

my_val = get_value_from_my_thing();
// do something with my_val

let_mut newval = get_value_from_my_thing();
while(newval == my_val) {
    cnx_this_thread_sleep_for(cnx_milliseconds(100));
    newval = get_value_from_my_thing();
}

my_val = newval;
// do something with new value

Classes

struct CnxUniqueLock
CnxUniqueLock provides scoped exclusive locking of any higher-level mutexes (eg CnxMutex, CnxSharedMutex, etc.) provided by Cnx. It allows for a simple, concise way to acquire the exclusive lock on a mutex and ensure that lock is released appropriately when the CnxUniqueLock exits scope.

Typedefs

using CnxUniqueLock = struct CnxUniqueLock
CnxUniqueLock provides scoped exclusive locking of any higher-level mutexes (eg CnxMutex, CnxSharedMutex, etc.) provided by Cnx. It allows for a simple, concise way to acquire the exclusive lock on a mutex and ensure that lock is released appropriately when the CnxUniqueLock exits scope.

Functions

void cnx_unique_lock_lock(CnxUniqueLock*restrict lock)
Acquires an exclusive lock on the mutex associated with the given CnxUniqueLock
bool cnx_unique_lock_try_lock(CnxUniqueLock*restrict lock)
Attempts to acquire an exclusive lock on the mutex associated with the given CnxUniqueLock
bool cnx_unique_lock_try_lock_for(CnxUniqueLock*restrict lock, CnxDuration duration)
Attempts to acquire an exclusive lock on the mutex associated with the given CnxUniqueLock
bool cnx_unique_lock_try_lock_until(CnxUniqueLock*restrict lock, CnxTimePoint stop_point)
Attempts to acquire an exclusive lock on the mutex associated with the given CnxUniqueLock
void cnx_unique_lock_unlock(CnxUniqueLock*restrict lock)
Unlocks the mutex associated with the given CnxUniqueLock
CnxMutexInterface cnx_unique_lock_mutex(CnxUniqueLock*restrict lock)
Returns the CnxMutexInterface Trait object corresponding with the mutex associated with the given CnxUniqueLock
bool cnx_unique_lock_owns_lock(CnxUniqueLock*restrict lock)
Returns whether the given CnxUniqueLock currently owns an exclusive lock on its associated mutex.
void cnx_unique_lock_free(void* lock)
Destroys the given CnxUniqueLock, unlocking its associated mutex if it owns a lock on it.

Defines

#define UniqueLock
Declaring a CnxUniqueLock as a UniqueLock ensures that it will be destroyed when it exits scope, releasing the lock on its associated mutex.
#define cnx_unique_lock(...)
Creates a new CnxUniqueLock associated with the given mutex.

Typedef documentation

typedef struct CnxUniqueLock CnxUniqueLock

CnxUniqueLock provides scoped exclusive locking of any higher-level mutexes (eg CnxMutex, CnxSharedMutex, etc.) provided by Cnx. It allows for a simple, concise way to acquire the exclusive lock on a mutex and ensure that lock is released appropriately when the CnxUniqueLock exits scope.

To ensure that a CnxUniqueLock is appropriately destroyed when it leaves scope, releasing the lock on its associated mutex, declare it as a UniqueLock.

A CnxUniqueLock should never be copied, to do so is undefined behavior. A CnxUniqueLock may be moved into a new scope with move (eg. passing it to a function as a parameter), however. If this is done though, the receiving function will need to either move the parameter into a local variable declared as a UniqueLock, or manually unlock the mutex before it returns.

Example:

// MyThing.h
#include <Cnx/sync/Mutex.h>
static MyType my_very_important_thing;
static CnxMutex* my_thing_mutex;

void init_my_thing(void);
void update_my_thing(u64 value);
u64 get_value_from_my_thing(void);

// MyThing.c
#include <Cnx/Allocators.h>
#include <Cnx/sync/UniqueLock.h>
#include "MyThing.h"
void init_my_thing(void) {
    if(my_thing_mutex == nullptr) {
        my_thing_mutex = cnx_allocator_allocate_t(CnxMutex, DEFAULT_ALLOCATOR);
        *my_thing_mutex = cnx_mutex_new();

        {
            // use a `CnxUniqueLock` to lock `my_thing_mutex`.
            // mark `lock` as `maybe_unused` so we don't get warnings about an unused variable
            __attr(maybe_unused) UniqueLock lock = cnx_unique_lock(*my_thing_mutex);
            my_very_important_thing = {
                // important intialization
            };
            // `lock` automatically destroyed at scope end, releasing the lock on
`my_thing_mutex`
        }
    }
}

void update_my_thing(u64 value) {
    __attr(maybe_unused) UniqueLock lock = cnx_unique_lock(*my_thing_mutex);
    my_very_important_thing.value = value;
    // `lock` automatically destroyed at scope end, releasing the lock on `my_thing_mutex`
}

u64 get_value_from_my_thing(CnxUniqueLock lock) {
    // move lock into lock2 so that the mutex will be unlocked at scope exit
    __attr(maybe_unused) UniqueLock lock2 = move(lock);
    let val = my_very_important_thing.value;
    return val;
    // `lock2` automatically destroyed at scope end, releasing the lock on `my_thing_mutex`
}


// do some compute intensive task...
// update the value
update_my_thing(new_value);

UniqueLock lock1 = cnx_unique_lock(*my_thing_mutex);
my_val = get_value_from_my_thing(move(lock1));
// do something with my_val

UniqueLock lock2 = cnx_unique_lock(*my_thing_mutex);
let_mut newval = get_value_from_my_thing(move(lock2));
while(newval == my_val) {
    cnx_this_thread_sleep_for(cnx_milliseconds(100));
    UniqueLock lock = cnx_unique_lock(*my_thing_mutex);
    newval = get_value_from_my_thing(move(lock));
}

my_val = newval;
// do something with new value

Function documentation

void cnx_unique_lock_lock(CnxUniqueLock*restrict lock)

Acquires an exclusive lock on the mutex associated with the given CnxUniqueLock

Parameters
lock - The CnxUniqueLock associated with the mutex to lock

bool cnx_unique_lock_try_lock(CnxUniqueLock*restrict lock)

Attempts to acquire an exclusive lock on the mutex associated with the given CnxUniqueLock

Parameters
lock - The CnxUniqueLock associated with the mutex to lock
Returns whether locking the mutex was successful

bool cnx_unique_lock_try_lock_for(CnxUniqueLock*restrict lock, CnxDuration duration)

Attempts to acquire an exclusive lock on the mutex associated with the given CnxUniqueLock

Parameters
lock - The CnxUniqueLock associated with the mutex to lock
duration - The maximum amount of time to spend trying to acquire the lock, after which acquisition will timeout and fail
Returns whether locking the mutex was successful

Attempts to acquire an exclusive lock on the mutex associated with lock. If the amount of time specified by duration has passed before successfully acquiring the lock, this will timeout and cease acquisition.

bool cnx_unique_lock_try_lock_until(CnxUniqueLock*restrict lock, CnxTimePoint stop_point)

Attempts to acquire an exclusive lock on the mutex associated with the given CnxUniqueLock

Parameters
lock - The CnxUniqueLock associated with the mutex to lock
stop_point - The point in time after which acquisition should timeout and fail, if the lock has not yet been acquired
Returns whether locking the mutex was successful

Attempts to acquire an exclusive lock on the mutex associated with lock. If the point in time specified by stop_point has passed before successfully acquiring the lock, this will timeout and cease acquisition.

void cnx_unique_lock_unlock(CnxUniqueLock*restrict lock)

Unlocks the mutex associated with the given CnxUniqueLock

Parameters
lock - The CnxUniqueLock associated with the mutex to unlock

CnxMutexInterface cnx_unique_lock_mutex(CnxUniqueLock*restrict lock)

Returns the CnxMutexInterface Trait object corresponding with the mutex associated with the given CnxUniqueLock

Parameters
lock - The CnxUniqueLock to get the associated mutex of
Returns the CnxMutexInterface Trait object of the mutex associated with the given lock

bool cnx_unique_lock_owns_lock(CnxUniqueLock*restrict lock)

Returns whether the given CnxUniqueLock currently owns an exclusive lock on its associated mutex.

Parameters
lock - The CnxUniqueLock to check if owns an exclusive lock on its mutex
Returns whether lock owns a lock on its mutex

void cnx_unique_lock_free(void* lock)

Destroys the given CnxUniqueLock, unlocking its associated mutex if it owns a lock on it.

Parameters
lock - The CnxUniqueLock to destroy

Define documentation

#define UniqueLock

Declaring a CnxUniqueLock as a UniqueLock ensures that it will be destroyed when it exits scope, releasing the lock on its associated mutex.

#define cnx_unique_lock(...)

Creates a new CnxUniqueLock associated with the given mutex.

Parameters
...
Returns a CnxUniqueLock constructed with the specified behavior

Creates a new CnxUniqueLock associated with the given mutex. If an instance of a tag type is NOT given to specify construction behavior, this will block until the exclusive lock is acquired on the given mutex. If an instance of a tag type specifying construction behavior IS given, however, then construction will behave according to the given tag type:

  • A cnx_defer_lock_t: Construct the CnxUniqueLock without acquiring the lock on the given mutex. The mutex will need to be manually locked (via the unique lock) later
  • A cnx_adopt_lock_t: The lock has already been acquired, so the CnxUniqueLock does not need to acquire it. Behavior will otherwise proceed as if no tag type instance were given.
  • A cnx_try_lock_t: Attempt to acquire the lock on the given mutex during construction. The CnxUniqueLock will need to be queried as to whether it successfully acquired the lock with cnx_unique_lock_owns_lock, but otherwise behavior will proceed as if no tag type instance were given.