Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idea Behind Recursive Mutex Lock

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;
}
like image 233
Davis Avatar asked Dec 08 '22 08:12

Davis


1 Answers

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.

like image 167
AllenKll Avatar answered Dec 11 '22 11:12

AllenKll