diff --git a/include/pal_base.h b/include/pal_base.h index f549ccf..3657b35 100644 --- a/include/pal_base.h +++ b/include/pal_base.h @@ -73,8 +73,9 @@ typedef p_ref_t p_event_t; typedef p_ref_t p_mem_t; typedef p_ref_t p_memptr_t; typedef p_ref_t p_atom_t; -typedef p_ref_t p_mutex_t; -typedef p_ref_t p_mutex_attr_t; + +typedef uint32_t * p_mutex_t; +typedef p_ref_t p_mutex_attr_t; /* *********************************************************************** @@ -172,7 +173,7 @@ int p_mutex_init(p_mutex_t *mp); int p_mutex_lock(p_mutex_t *mp); /*Try locking a mutex once*/ -int p_mutex_trylock(p_mutex_t *mp); +int p_mutex_trylock(p_mutex_t *p); /*Unlock a mutex*/ int p_mutex_unlock(p_mutex_t *mp); diff --git a/src/base/p_mutex_destroy.c b/src/base/p_mutex_destroy.c index 6f24ca0..353b2ea 100644 --- a/src/base/p_mutex_destroy.c +++ b/src/base/p_mutex_destroy.c @@ -3,9 +3,33 @@ #include "pal_base.h" #include "pal_base_private.h" +/* Clean up and destroy a p_mutex_t + * @param mp A pointer to a p_mutex_t. + * @returns 0 on success, + * EINVAL when mp is NULL or uninitialized. + * EBUSY when the mutex has not been unlocked beforehand. + */ int p_mutex_destroy(p_mutex_t *mp) { + /* + * Conditions: + * mp must NOT be NULL (it is a pointer pointer!) + * *mp must NOT be NULL (that would mean it hasn't been initialized yet) + * **mp must NOT be Non-Zero (that would mean we're trying to unlock a locked mutex.) + */ - /*PLACE CODE HERE*/ - return (0); + // Check if mp or *mp are null. + // If they are not null, check that the mutex is not locked. + if(mp == NULL || *mp == NULL) { + return EINVAL; + } + else if(**mp !=0) { + return EBUSY; + } + + // at this point, the given value should be fine. + free(*mp); + // Be nice and clean the part of memory that we're touching. + *mp = NULL; + return 0; } diff --git a/src/base/p_mutex_init.c b/src/base/p_mutex_init.c index fa4b0f3..fd61d78 100644 --- a/src/base/p_mutex_init.c +++ b/src/base/p_mutex_init.c @@ -3,10 +3,27 @@ #include "pal_base.h" #include "pal_base_private.h" -int p_mutex_init(p_mutex_t *mp) +/* + * Initalize a mutex. + * + * This makes no attempt to make sure you're not clobbering an existing mutex. + * If for some reason you decide to pass this a locked, in-use mutex, it will + * happily trapse all over it and take your determinism right with it. + * + * This WILL make a cursory check to make sure malloc succeeded. + * + * @returns 0 on success, EINVAL if the p_mutex_t* is bad (NULL) + * + */ +int p_mutex_init(p_mutex_t * mp) { - - /*PLACE CODE HERE*/ - - return (0); + p_mutex_t tmp; + if(mp == NULL) { + return EINVAL; + } + *mp = malloc(sizeof(**mp)); + if(*mp == NULL) { + return EINVAL; + } + **mp = 0; } diff --git a/src/base/p_mutex_lock.c b/src/base/p_mutex_lock.c index 2ef62b7..13722f4 100644 --- a/src/base/p_mutex_lock.c +++ b/src/base/p_mutex_lock.c @@ -3,9 +3,46 @@ #include "pal_base.h" #include "pal_base_private.h" +/* Take lock of a mutex. + * + * This will make a cursory check that you aren't passing NULL or a pointer + * to NULL in it. It won't check if you've been an idiot and passed in + * a uint32_t** because you aren't using the typedef. + * + * This is a blocking call. It will sit, chewing away at CPU time until it + * gets the mutex. It will constantly dereference the pointer you've handed + * it. + * + * If you destroy the mutex you are attempting to lock with this while the + * a call to this is going, you will segfault. If you don't segfault, + * it will still make no checks to see that you haven't quietly reallocated + * that bit of memory. + * + * @returns 0 on success, EINVAL if you pass it NULL before locking. + */ int p_mutex_lock(p_mutex_t *mp) { - /*PLACE CODE HERE*/ - + if(mp == NULL || *mp == NULL) { + return EINVAL; + } + /* We've got to avoid race conditions. Our solution to this is to spin + * until we can increment the mutex and make it 1, not 2. + * This works because our definition of "locked" is non-zero + * and our unlock is set to 0. + * + * When we lose the race, our mutex will be a value other than 1. + * + * When we enter the loop, we spin until **mp == 0 + * When **mp == 0, we check if ++(**mp) == 1 + * If that condition is true, we are safe: We won the race. + * If that condition is false, we lost the race: keep trying. + */ + do { + /* Spin while the lock is taken. */ + while(**mp != 0) { ;; } + /* attempt to take the lock. If we lost the race + * we'll keep trying. + */ + } while( ++(**mp) != 1); return (0); } diff --git a/src/base/p_mutex_trylock.c b/src/base/p_mutex_trylock.c index 2b83739..949ea9f 100644 --- a/src/base/p_mutex_trylock.c +++ b/src/base/p_mutex_trylock.c @@ -3,10 +3,29 @@ #include "pal_base.h" #include "pal_base_private.h" +/* attempts to consume the mutex mp + * + * This will quickly make an attempt to grab the mutex you are handing it. + * It makes a cursory check to see that you aren't passing in NULL or + * an uninitialized mutex. + * + * @param mp pointer to a p_mutex_t + * @returns 0 on success, EINVAL when passed NULL, EBUSY if you lose the race. + */ int p_mutex_trylock(p_mutex_t *mp) { - - /*PLACE CODE HERE*/ - - return (0); + if( mp == NULL || *mp == NULL) { + return EINVAL; + } + if(**mp !=0 ) { return EBUSY; } + /* Try to take the mutex. If we won, **mp will be 1. If we lost, + * it'll be something different. + * + * There's one small caveat to this: If, by some magical means, you have + * the ability to make UINT_MAX attempts *simultaneously* + * you run the slight risk of overflowing. + * + * That's a lot of attempts, though. + */ + return ( ++(**mp) == 1 ? 0 : EBUSY ); } diff --git a/src/base/p_mutex_unlock.c b/src/base/p_mutex_unlock.c index 23890e5..5930a41 100644 --- a/src/base/p_mutex_unlock.c +++ b/src/base/p_mutex_unlock.c @@ -3,10 +3,23 @@ #include "pal_base.h" #include "pal_base_private.h" +/* + * Unlock a mutex. + * + * This makes a cursory check to see if you're passing it a bad value. + * + * @param mp the mutex to unlock + * @returns 0 on success, EINVAL if mp is NULL or uninitialized. + * + */ int p_mutex_unlock(p_mutex_t *mp) { - - /*PLACE CODE HERE*/ - - return (0); + if(mp == NULL || *mp == NULL) { + return EINVAL; + } + else + { + **mp = 0; + return (0); + } } diff --git a/src/base/pal_base_private.h b/src/base/pal_base_private.h index 9c5431b..e03b07f 100644 --- a/src/base/pal_base_private.h +++ b/src/base/pal_base_private.h @@ -48,14 +48,11 @@ struct p_event struct p_atom_u32 { - uint32_t mutex; // resource mutex + p_mutex_t mutex; // resource mutex uint32_t var; // atomic variable }; -struct p_mutex -{ - uint32_t mutex; // mutex -}; + /* ***********************************************************************