CnxRecursiveMutex struct

CnxRecursiveMutex is a higher-level mutex type, directly comparable to C++'s std::recursive_mutex`, for use when an algorithm requires that a thread be able to lock the same mutex multiple times in its control flow. While such an algorithm would cause the thread to deadlock itself when used with a normal mutex, this is the intended task for a recursive mutex. For example, if a recursive algorithm requires synchronization using the same mutex at multiple levels of recursion, and one level of the algorithm can't release the lock before calling the next, a CnxRecursiveMutex would make this type of algorithm possible and prevent the thread from deadlocking itself.

Example:

// MyThing.h
#include <Cnx/sync/Mutex.h>
static MyType my_very_important_thing;
static CnxRecursiveMutex* 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 "MyThing.h"
void init_my_thing(void) {
    if(my_thing_mutex == nullptr) {
        my_thing_mutex = cnx_allocator_allocate_t(CnxRecursiveMutex, DEFAULT_ALLOCATOR);
        *my_thing_mutex = cnx_recursive_mutex_new();

        cnx_recursive_mutex_lock(my_thing_mutex);
        my_very_important_thing = {
        // important intialization
        };
        cnx_recursive_mutex_unlock(my_thing_mutex);
    }
}

void update_my_thing(u64 value) {
    cnx_mutex_lock(my_thing_mutex);
    let adjusted_value = static_cast(u64)(4 * value);
    if(adjusted_value < 46) {
        // even though we've already acquired the lock on `my_thing_mutex`, and this will try to
        // acquire it again, that's OK because we're using a recursive mutex
        update_my_thing(adjusted_value);
    }
    else {
        my_very_important_thing.value = adjusted_value;
    }
    cnx_mutex_unlock(my_thing_mutex);
}

u64 get_value_from_my_thing(void) {
    cnx_mutex_lock(my_thing_mutex);
    let val = my_very_important_thing.value;
    cnx_mutex_unlock(my_thing_mutex);
    return val;
}


// 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