0

I was wondering if I could make a recursive mutex type on my own with a PTHREAD_MUTEX_ERRORCHECK mutex, this is the result:

typedef struct {
    pthread_mutex_t mutex;
    uint32_t deadlocks;
} pthread_recursivemutex_t;

int pthread_recursivemutex_init(pthread_recursivemutex_t *mutex)
{
    int ret;
    pthread_mutexattr_t attr;

    mutex->deadlocks = 0;

    ret = pthread_mutexattr_init(&attr);

    if (ret != 0) {
        return ret;
    }

    (void)pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);

    ret = pthread_mutex_init(&mutex->mutex, &attr);

    (void)pthread_mutexattr_destroy(&attr);

    return ret;
}

void pthread_recursivemutex_lock(pthread_recursivemutex_t *mutex)
{
    int ret;    

    ret = pthread_mutex_lock(&mutex->mutex);

    if (ret == 0) {
        mutex->deadlocks = 0;
    } else if (ret == EDEADLK) {
        mutex->deadlocks += 1;
    }
}

void pthread_recursivemutex_unlock(pthread_recursivemutex_t *mutex)
{
    if (mutex->deadlocks == 0) {
        (void)pthread_mutex_unlock(&mutex->mutex);
    } else {
        mutex->deadlocks -= 1;
    }
}

void pthread_recursivemutex_destroy(pthread_recursivemutex_t *mutex)
{
    (void)pthread_mutex_destroy(&mutex->mutex);
}

I found out that this type of recursive mutex is a lot faster than a mutex with the PTHREAD_MUTEX_RECURSIVE attribute:

iterations               : 1000000

pthread_mutex_t          : 71757 μSeconds
pthread_recursivemutex_t : 48583 μSeconds

Test code (each called 1000000 times):

void mutex_test()
{
    pthread_mutex_lock(&recursiveMutex);
    pthread_mutex_lock(&recursiveMutex);
    pthread_mutex_unlock(&recursiveMutex);
    pthread_mutex_unlock(&recursiveMutex);
}

void recursivemutex_test()
{
    pthread_recursivemutex_lock(&myMutex);
    pthread_recursivemutex_lock(&myMutex);
    pthread_recursivemutex_unlock(&myMutex);
    pthread_recursivemutex_unlock(&myMutex);
}

pthread_recursivemutex_t is almost twice as fast as pthread_mutex_t ?! But both behave the same way...?

BTW:

pthread_mutex_t my_mutex;

void *m(void*a)
{
    printf("m: %d %d\n", pthread_mutex_lock(&my_mutex), EDEADLK);
    usleep(500 * 1000);
    printf("m: %d\n", pthread_mutex_lock(&my_mutex));
    pthread_mutex_unlock(&my_mutex);

    return a;
}

void *m2(void *a)
{
    printf("UNLOCKED!!: %d\n", pthread_mutex_lock(&my_mutex));
    usleep(500 * 1000);
    printf("m2: %d\n", pthread_mutex_lock(&my_mutex));

    return a;
}

int main(int argc, const char * argv[])
{
    pthread_t t,t2;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
    pthread_mutex_init(&my_mutex, &attr);

    pthread_create(&t, NULL, m, NULL);
    pthread_create(&t2, NULL, m2, NULL);

    pthread_join(t, NULL);
    pthread_join(t2, NULL);

    return 0;
}

Gives

m: 0 11 m: 11 UNLOCKED!!: 0 m2: 11

Which means thread 2 has no chance to get the mutex while its locked.

jotik
  • 17,044
  • 13
  • 58
  • 123
Marco
  • 7,007
  • 2
  • 19
  • 49
  • I'm struggling to find a question to answer here. What you are doing appears to be POSIX compliant. (I confess that depending on what seems to be an error return goes against the grain... but the `EDEADLK` and `PTHREAD_MUTEX_ERRORCHECK` are well defined AFAICS.) For completeness, I note that you are not checking for other errors returned by `pthread_mutex_xxx()` –  Jul 22 '14 at 12:53
  • Thanks for pointing that out, I did not check the other return statements because I would make later on a panic function which kills the Process if anything run not as suspected. My question is: is the above code safe and compliant? And why is it faster than the normal recursive mutex (I know an unlock by another thread would be undefined behavior in im implementation, recursive mutexes by POSIX would return EPERM AFAIK). – Marco Jul 22 '14 at 13:37
  • 1
    It does appear to be compliant. I would check all the return codes to make sure that the speed improvement is not a side effect of something else. Why it should be faster... sorry, dunno... but I observe that there is one less call of `pthread_mutex_unlock()` ! –  Jul 22 '14 at 14:35
  • I think checking the return value would not make a big difference in performance. But why is the function supplied by POSIX so slow compared to mine (I mean implementation by apple) ? – Marco Jul 22 '14 at 20:30
  • I don't suppose checking return codes will change much either. But given an unexplained performance improvement, I think it's worth being sure that the faster code isn't faster because it's not, in fact, working properly... and hence is not actually doing the same thing ! –  Jul 23 '14 at 15:42
  • Why its not doing the same thing? – Marco Jul 23 '14 at 16:33
  • I'm sorry, I'm obviously expressing this badly... If there are errors being reported which you are not checking for, then it won't be doing the same thing, it will be failing. –  Jul 23 '14 at 16:38
  • I mean by its **functionality** not what it does return. – Marco Jul 23 '14 at 17:03

0 Answers0