1
votes

I read that the mutex should be unlocked by same thread that has locked it. Let us consider the following Scenario.

I have a mutex variable say myMute, T1, T2 are two threads.

  1. T1 locks the myMute.

  2. T2 unlocks myMute.

  3. T2 locks myMute.

  4. T1 unlocks myMute.

Is this ordered locking/unlocking from different threads a valid approach?

1
No, that defeats the entire purpose of having a Mutex which is to protect a section of code from being interrupted by another section of code. As you read it should only be unlocked by the code that locked it. It sounds like you have a different problem - maybe you should post the actual problem and ask for the correct solution?Dale K
"Should" is an understatement. A mutex can only be unlocked by the thread that currently holds it. Any attempt to unlock it by another thread results in potentially-very-dangerous undefined behavior.R.. GitHub STOP HELPING ICE
"A valid approach" to what? What is it you're really trying to do?Michael Burr

1 Answers

5
votes

No, this is incorrect. From pthread_mutex_lock man page:

If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, undefined behavior results.

Example correct ordering:

  • T1 locks myMutex
  • T2 locks myMutex (blocked waiting for T1 to unlock the mutex)
  • T1 unlocks myMutex (T2 now locks mutex)
  • T2 unlocks myMutex

EDIT:

Small example using pthread_cond_wait() with error checking omitted for brevity:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

volatile int value = 0;
pthread_mutex_t mymutex;
pthread_t thread_one;
pthread_t thread_two;
pthread_cond_t cond;

void* main_one(void* ignored)
{
    while (value < 10)
    {
        pthread_mutex_lock(&mymutex);
        pthread_cond_wait(&cond, &mymutex);
        fprintf(stderr, "T1: value=%d\n", value);
        pthread_mutex_unlock(&mymutex);
    }
    return (void*)0;
}

void* main_two(void* ignored)
{
    int i;

    for (i = 0; i < 10; i++)
    {
        pthread_mutex_lock(&mymutex);
        value++;
        fprintf(stderr, "T2: value=%d\n", value);
        pthread_cond_broadcast(&cond);
        fprintf(stderr, "Broadcasted but T1 cannot continue for 1 second\n");
        sleep(1);
        pthread_mutex_unlock(&mymutex);
        pthread_yield();
    }

    return (void*)0;
}

void start_thread(void* (*a_entry_point)(void*),
                  pthread_t* a_handle)
{
    pthread_attr_t thread_attributes;

    pthread_attr_init(&thread_attributes);
    pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_JOINABLE);
    pthread_create(a_handle, &thread_attributes, a_entry_point, 0);
}

int main()
{
    pthread_mutexattr_t attr;
    pthread_t thread_one_handle;
    pthread_t thread_two_handle;

    /* Init mutex. */
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
    pthread_mutex_init(&mymutex, &attr);

    /* Init condition. */
    pthread_cond_init(&cond, 0);

    /* Start threads. */
    start_thread(main_one, &thread_one_handle);
    start_thread(main_two, &thread_two_handle);

    /* Wait for threads. */
    pthread_join(thread_one_handle, 0);
    pthread_join(thread_two_handle, 0);

    /* Clean up. */
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mymutex);

    return 0;
}

Compiled with gcc -Wall -Werror -D_GNU_SOURCE main.c -o main -pthread.