Martin
Martin

Reputation: 223

Wait on a Mutex without Conditions

I have an assignment on where I need to do some thread management. The problem is that I'm only allowed to use one mutex, nothing more, no more conditions or anything else.

I have a struct which contains some data which 2 threads change and a mutex. The struct is arranged in an array and is accessed via the index.

EDIT: I think this is a better explanation. The problem is that I have a deadlock because thread2 still holds a lock and thread1 wants to acquire it, or sometimes the opposite.

typedef struct {
  int number;
  pthread_mutex_t mutex;
} data;

data myStruct[100];

void* thread1()
{
  pthread_mutex_lock(&myStruct[index].mutex)
// get 1 index value
// access the struct via index
// change a value
  pthread_mutex_unlock(&myStruct[index].mutex)
}

void* thread2()
{
  pthread_mutex_lock(&myStruct[index1].mutex)
  pthread_mutex_lock(&myStruct[index2].mutex)
// get 2 index values
// access struct with both indexes
// change values with both indexes
  pthread_mutex_unlock(&myStruct[index2].mutex)
  pthread_mutex_unlock(&myStruct[index1].mutex)
}

The index values are obtained via some random calculations. Mutex initialisation and destruction, and thread creation are not in my hands.

I have 2 threads. Both threads can access the struct and the mutex within. and have to use this mutex to lock and unlock the data. Thread 1 only changes data from one data struct, thread 2 changes data from two data structs.

The problem is that I have to wait for mutexes in both threads, and at the moment I solved it via

while(pthread_mutex_lock(&struct[i].lock) != 0)

It works sometimes, but I have no better idea how I should do this locking only with this single mutex. I'm not allowed to make more mutexes, semaphores or conditions.

Do you have any ideas what I could try?

Upvotes: 1

Views: 1013

Answers (3)

Andre Kampling
Andre Kampling

Reputation: 5630

The pthread_mutex_lock(&struct[i].lock) call blocks by itself if the mutex is locked by another thread. No need using the polling while loop. See manpage:

The mutex object referenced by mutex shall be locked by calling pthread_mutex_lock(). If the mutex is already locked, the calling thread shall block until the mutex becomes available.

Also remember unlocking the mutex by pthread_mutex_unlock((&struct[i].lock).

Edit #1:

You're using multiple mutexes because you have declared an array: data myStruct[100]. So you have to unlock the right mutex or just use one mutex for the whole array.

Edit #2:

Your code looks right, so it seems that your thread never reaches the unlock statement, you should search for that.

If you want to use just one mutex you could something like this

typedef struct {
    int* number;
    pthread_mutex_t mutex;
} data;

where int* number is a pointer to an array of data. Sure this pointer have to point to valid data.

Upvotes: 1

Brendan
Brendan

Reputation: 37262

I'd assume that the entire point of this exercise was to teach you the concept of "global lock order".

The idea is that you give all your locks an order, and when acquiring 2 or more locks make sure you acquire them in that order, and when releasing them make sure you release them in the opposite order. This makes sure that you can never get into a "thread1 has lock1 and wants lock2, but thread2 has lock2 and wants thread1" style deadlock.

In practical terms, this might look something like:

void* thread2()
{
    if(index1 < index2) {
        temp = index1; index1 = index2; index2 = temp;
    }
    pthread_mutex_lock(&myStruct[index1].mutex)
    pthread_mutex_lock(&myStruct[index2].mutex)
    // get 2 index values
    // access struct with both indexes
    // change values with both indexes
    pthread_mutex_unlock(&myStruct[index2].mutex)
    pthread_mutex_unlock(&myStruct[index1].mutex)
}

Or (if index1 and index2 aren't interchangeable):

void* thread2()
{
    if(index1 < index2) {
        pthread_mutex_lock(&myStruct[index1].mutex)
        pthread_mutex_lock(&myStruct[index2].mutex)
        // get 2 index values
        // access struct with both indexes
        // change values with both indexes
        pthread_mutex_unlock(&myStruct[index2].mutex)
        pthread_mutex_unlock(&myStruct[index1].mutex)
     } else {
        pthread_mutex_lock(&myStruct[index2].mutex)
        pthread_mutex_lock(&myStruct[index1].mutex)
        // get 2 index values
        // access struct with both indexes
        // change values with both indexes
        pthread_mutex_unlock(&myStruct[index1].mutex)
        pthread_mutex_unlock(&myStruct[index2].mutex)
     }
}

Upvotes: 0

Your code seems like a potential for deadlock. If thread1 function acquires the first mutex and thread2 function acquires the second, all resources may be tied up and locked. I think you should try something like that,

...
pthread_mutex_lock(&mutex_1);
while ( pthread_mutex_trylock(&mutex_2) )  /* Test if already locked   */
{
   pthread_mutex_unlock(&mutex_1);  /* Free resource to avoid deadlock */
   ...
   /* stall here   */
   ...
   pthread_mutex_lock(&mutex_1);
}
// count++; or yadda yadda yadda
pthread_mutex_unlock(&mutex_1);
pthread_mutex_unlock(&mutex_2);
...

Upvotes: 1

Related Questions