I'm working on a school lab and we are instructed to create a recursive mutex lock for a counting program. I've written some code (which doesn't work), but I think that this is mostly because I do not understand the real idea behind using a recursive mutex lock. Could anyone elaborate what a recursive mutex lock should do/look like?
General Note: I'm not asking for an answer, just some clarification as to what recursive mutex lock should do.
Also, if anyone is curious, here is the code required for this. The code that I am editing/implementing is the recmutex.c.
recmutex.h
#include <pthread.h>
/*
* The recursive_mutex structure.
*/
struct recursive_mutex {
pthread_cond_t cond;
pthread_mutex_t mutex; //a non-recursive pthread mutex
pthread_t owner;
unsigned int count;
unsigned int wait_count;
};
typedef struct recursive_mutex recursive_mutex_t;
/* Initialize the recursive mutex object.
*Return a non-zero integer if errors occur.
*/
int recursive_mutex_init (recursive_mutex_t *mu);
/* Destroy the recursive mutex object.
*Return a non-zero integer if errors occur.
*/
int recursive_mutex_destroy (recursive_mutex_t *mu);
/* The recursive mutex object referenced by mu shall be
locked by calling pthread_mutex_lock(). When a thread
successfully acquires a mutex for the first time,
the lock count shall be set to one and successfully return.
Every time a thread relocks this mutex, the lock count
shall be incremented by one and return success immediately.
And any other calling thread can only wait on the conditional
variable until being waked up. Return a non-zero integer if errors occur.
*/
int recursive_mutex_lock (recursive_mutex_t *mu);
/* The recursive_mutex_unlock() function shall release the
recursive mutex object referenced by mu. Each time the owner
thread unlocks the mutex, the lock count shall be decremented by one.
When the lock count reaches zero, the mutex shall become available
for other threads to acquire. If a thread attempts to unlock a
mutex that it has not locked or a mutex which is unlocked,
an error shall be returned. Return a non-zero integer if errors occur.
*/
int recursive_mutex_unlock (recursive_mutex_t *mu);
recmutex.c: contains the functions for the recursive mutex
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include "recmutex.h"
int recursive_mutex_init (recursive_mutex_t *mu){
int err;
err = pthread_mutex_init(&mu->mutex, NULL);
if(err != 0){
perror("pthread_mutex_init");
return -1;
}else{
return 0;
}
return 0;
}
int recursive_mutex_destroy (recursive_mutex_t *mu){
int err;
err = pthread_mutex_destroy(&mu->mutex);
if(err != 0){
perror("pthread_mutex_destroy");
return -1;
}else{
return 1;
}
return 0;
}
int recursive_mutex_lock (recursive_mutex_t *mu){
if(mutex_lock_count == 0){
pthread_mutex_lock(&mu->mutex);
mu->count++;
mu->owner = pthread_self();
printf("%s", mu->owner);
return 0;
}else if(mutex_lock_count > 0){
pthread_mutex_lock(&mu->mutex);
mu->count++;
mu->owner = pthread_self();
return 0;
}else{
perror("Counter decremented incorrectly");
return -1;
}
}
int recursive_mutex_unlock (recursive_mutex_t *mu){
if(mutex_lock_count <= 0){
printf("Nothing to unlock");
return -1;
}else{
mutex_lock_count--;
pthread_mutex_unlock(&mu->mutex);
return 0;
}
}
count_recursive.cc: The counting program mentioned above. Uses the recmutex functions.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include "recmutex.h"
//argument structure for the thread
typedef struct _arg_{
int n1;
int n2;
int ntimes;
}Arg;
int count; //global counter
recursive_mutex_t mutex; //the recursive mutex
void do_inc(int n){
int ret;
if(n == 0){
return;
}else{
int c;
ret = recursive_mutex_lock(&mutex);
assert(ret == 0);
c = count;
c = c + 1;
count = c;
do_inc(n - 1);
ret = recursive_mutex_unlock(&mutex);
assert(ret == 0);
}
}
/* Counter increment function. It will increase the counter by n1 * n2 * ntimes. */
void inc(void *arg){
Arg * a = (Arg *)arg;
for(int i = 0; i < a->n1; i++){
for(int j = 0; j < a->n2; j++){
do_inc(a->ntimes);
}
}
}
int isPositiveInteger (const char * s)
{
if (s == NULL || *s == '\0' || isspace(*s))
return 0;
char * p;
int ret = strtol (s, &p, 10);
if(*p == '\0' && ret > 0)
return 1;
else
return 0;
}
int test1(char **argv){
printf("==========================Test 1===========================\n");
int ret;
//Get the arguments from the command line.
int num_threads = atoi(argv[1]); //The number of threads to be created.
int n1 = atoi(argv[2]); //The outer loop count of the inc function.
int n2 = atoi(argv[3]); //The inner loop count of the inc function.
int ntimes = atoi(argv[4]); //The number of increments to be performed in the do_inc function.
pthread_t *th_pool = new pthread_t[num_threads];
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
ret = recursive_mutex_init(&mutex);
assert(ret == 0);
printf("Start Test. Final count should be %d\n", num_threads * n1 * n2 * ntimes );
// Create threads
for(int i = 0; i < num_threads; i++){
Arg *arg = (Arg *)malloc(sizeof(Arg));
arg->n1 = n1;
arg->n2 = n2;
arg->ntimes = ntimes;
ret = pthread_create(&(th_pool[i]), &attr, (void * (*)(void *)) inc, (void *)arg);
assert(ret == 0);
}
// Wait until threads are done
for(int i = 0; i < num_threads; i++){
ret = pthread_join(th_pool[i], NULL);
assert(ret == 0);
}
if ( count != num_threads * n1 * n2 * ntimes) {
printf("\n****** Error. Final count is %d\n", count );
printf("****** It should be %d\n", num_threads * n1 * n2 * ntimes );
}
else {
printf("\n>>>>>> O.K. Final count is %d\n", count );
}
ret = recursive_mutex_destroy(&mutex);
assert(ret == 0);
delete [] th_pool;
return 0;
}
int foo(){
int ret;
printf("Function foo\n");
ret = recursive_mutex_unlock(&mutex);
assert(ret != 0);
return ret;
}
//test a thread call unlock without actually holding it.
int test2(){
int ret;
printf("\n==========================Test 2==========================\n");
pthread_t th;
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
ret = recursive_mutex_init(&mutex);
ret = pthread_create(&th, &attr, (void * (*)(void *))foo, NULL);
printf("Waiting for thread to finish\n");
ret = pthread_join(th, NULL);
assert(ret == 0);
return 0;
}
int main( int argc, char ** argv )
{
int ret;
count = 0;
if( argc != 5 ) {
printf("You must enter 4 arguments. \nUsage: ./count_recursive num_threads n1 n2 ntimes\n");
return -1;
}
if(isPositiveInteger(argv[1]) != 1 || isPositiveInteger(argv[2]) != 1 || isPositiveInteger(argv[3]) != 1 || isPositiveInteger(argv[4]) != 1 ){
printf("All the 4 arguments must be positive integers\n");
return -1;
}
test1(argv);
test2();
return 0;
}
The idea of a recursive mutex is that it can be successfully relocked by the thread that is currently holding the lock. For example:
if I had some mutexes like this (this is pseudocode):
mutex l;
recursive_mutex r;
In a single thread if I did this:
l.lock();
l.lock(); // this would hang the thread.
but
r.lock();
r.lock();
r.lock(); // this would all pass though with no issue.
In implimenting a recursive mutex you need to check what threadId has locked it, if it was locked, and if it matches the current thread id, return success.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With