Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pthread_mutex_timedlock() exiting prematurely without waiting for timeout

I want to protect a function from multithreaded access. For that purpose I am using a pthread_mutex_t mutex. I try to lock it in the beginning of a function, then execute the function, then release it again. If the mutex is in use it should wait for at maximum 60 seconds for it to be come available. If after that it is still not available, the function should fail.

The problem I'm having is it that pthread_mutex_timedlock seems to completely ignore the timeout value I'm giving it. Although I specify a timeout of 60 seconds, if the lock is taken, the function returns immediately with the error code ETIMEDOUT -- without actually waiting.

Here is a minimal example which reproduces the problem. In this case it does not matter whether I'm using recursive or non-recursive mutexes, since I'm not trying to lock them multiple times from the same thread.

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stddef.h> 
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>

pthread_mutex_t lock; /* exclusive lock */

//do some work to keep the processor busy..
int wut() {
    int x = 0;
    for(int i=0; i < 1024*1024*1024; i++)
        x += 1;
    return x;
}

void InitMutex(){
    /*pthread_mutexattr_t Attr;
    pthread_mutexattr_init(&Attr);
    pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&lock, &Attr);*/
    pthread_mutex_init(&lock, NULL);
}

//lock mutex, wait at maximum 60 seconds, return sucesss
int LockMutex() {
    struct timespec timeoutTime;
    timeoutTime.tv_nsec = 0;
    timeoutTime.tv_sec = 60;
    printf("Nanoseconds: %lu, seconds %lu\n", timeoutTime.tv_nsec, timeoutTime.tv_sec);
    int retVal = pthread_mutex_timedlock(&lock, &timeoutTime);
    printf("pthread_mutex_timedlock(): %d\n", retVal);
    if(retVal != 0) {
        const char* errVal = NULL;
        switch(retVal) {
        case EINVAL: errVal = "EINVAL"; break;
        case EAGAIN: errVal = "EAGAIN"; break;
        case ETIMEDOUT: errVal = "ETIMEDOUT"; break;
        case EDEADLK: errVal = "EDEADLK"; break;
        default: errVal = "unknown.."; break;
        }
        printf("Error taking lock in thread %lu: %s (%s)\n", pthread_self(), errVal , strerror(retVal));
    }
    return retVal == 0; //indicate success/failure
}

void UnlockMutex() {
    pthread_mutex_unlock(&lock);
}

void TestLockNative() {
    uint64_t thread_id = pthread_self();
    printf("Trying to take lock in thread %lu.\n", thread_id);
    int ret = LockMutex();
    printf("Got lock in thread %lu. sucess=%d\n", thread_id, ret);
    wut();
    printf("Giving up lock now from thread %lu.\n", thread_id);
    UnlockMutex();

}

void* test_thread(void* arg) {
    //TestLock();
    TestLockNative();
    return NULL;
}

int main() {
    InitMutex();
    //create two threads which will try to access the protected function at once
    pthread_t t1, t2;
    pthread_create(&t1, NULL, &test_thread, NULL);
    pthread_create(&t2, NULL, &test_thread, NULL);

    //wait for threads to end
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return 0;
}

The output of the program is e.g.:

Trying to take lock in thread 139845914396416.
Nanoseconds: 0, seconds 6000
pthread_mutex_timedlock(): 0
Got lock in thread 139845914396416. sucess=1
Trying to take lock in thread 139845906003712.
Nanoseconds: 0, seconds 6000
pthread_mutex_timedlock(): 110
Error taking lock in thread 139845906003712: ETIMEDOUT (Connection timed out) [<-- this occurs immediately, not after 60 seconds]
Got lock in thread 139845906003712. sucess=0
Giving up lock now from thread 139845906003712.

Compilation with gcc -o test test.c -lpthread should work.

So, does anyone know what's going on here and why pthread_mutex_timedlock() ignores my timeout value? It does not behave the way it is documented at all.

I'm using a Ubuntu 16.04.2 LTS system, compiling with gcc.

like image 802
Maximilian Gerhardt Avatar asked Sep 22 '17 13:09

Maximilian Gerhardt


People also ask

Which one of the given error code will not return by the Pthread_mutex_timedlock () function?

The protected state of the mutex cannot be recovered. The mutex is a robust mutex, and the process of the thread that owns the mutex terminated while holding the mutex lock. This function does not return the EINTR error code.

Which of the given below statement S is are need to be provided on all Pthread_mutex_timedlock () function implementations?

APPLICATION USAGE. The pthread_mutex_timedlock() function is part of the Threads and Timeouts options and need not be provided on all implementations.


1 Answers

The manual page for pthread_mutex_timedlock says:

The timeout shall expire when the absolute time specified by abstime passes, as measured by the clock on which timeouts are based

Therefore, use real time to specify your timeout value:

int LockMutex() {
    struct timespec timeoutTime;
    clock_gettime(CLOCK_REALTIME, &timeoutTime);
    timeoutTime.tv_sec += 60;

    int retVal = pthread_mutex_timedlock(&lock, &timeoutTime);
    ....
like image 185
LWimsey Avatar answered Sep 28 '22 09:09

LWimsey