0

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?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Martin
  • 221
  • 3
  • 14
  • 1
    I guess you mean you can only use one mutex *per struct*. That's very different from "i'm only allowed to use one mutex, nothing more." – John Bollinger May 26 '17 at 13:10
  • 1
    Anyway, it's unclear what the nature of the problem is. You imply that sometimes what you're doing *doesn't* work, but what happens in that case? Overall, we will probably need to see a [mcve] to help you determine the problem. – John Bollinger May 26 '17 at 13:13
  • 1
    I don't understand what your problem is. You describe "the problem" as multiple threads having to wait for mutexes. That is not a problem, that is the solution. You lock a mutex (which is a blocking call) do the modifications and unlock it. If all threads play nice, there is no problem whatsoever. You only have problems if threads don't unlock their mutexes. – Cheatah May 26 '17 at 13:19
  • the problem that when im doing it just like the answer below is saying, the programm just stops working, the unlocking is also in place. on thing, i can't post the code because that violates the rules of this lecture. – Martin May 26 '17 at 13:42
  • Then try to provide more accurate explanation maybe with code that does not violate the rules of your lecture. – Andre Kampling May 26 '17 at 13:44
  • Since you have changed your post, let me add something. Like I said in my previous comment, you only have problems if threads don't unlock their mutexes. Have thread2 play nice by not keeping a lock indefinitely in case it cannot get the second, I believe that using pthread_mutex_trylock for the "inner lock" does not violate the rules you provided. It does however affect whether all threads get similar opportunity. But I guess it beats a deadlock. – Cheatah May 26 '17 at 14:16
  • potential lock over already locked one? – Soner from The Ottoman Empire May 26 '17 at 14:30
  • 'I'm not allowed to make more mutexes, semaphores or conditions' well, that's not going to be useful to future visitors to SO. Your homework restrictions are very unlikely to apply to such visitors. I'm surprised that you are only at -1. – ThingyWotsit May 26 '17 at 15:04
  • Note that you have three global variables `index`, `index1`, `index2` and no indication of what their values are — are they all distinct? Your thread functions do not have the correct signature. The code you show doesn't return a value from the thread functions — it should (even if it is just `return 0;`). – Jonathan Leffler May 27 '17 at 05:13

3 Answers3

1

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.

Andre Kampling
  • 5,476
  • 2
  • 20
  • 47
1

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);
...
  • this was the sollution, but i don't konw exactly what this is doing and why this is working, can you please explain it a little more ? – Martin May 26 '17 at 16:13
  • While this is a solution, it's not the solution (less efficient and more susceptible to a different problem called "livelock"). – Brendan May 26 '17 at 16:22
  • Even though I'm too much new parallel programmer cadidate, I will try to explain. As most likey you read or know, threads are scheduled by the operating system and are executed at random. We use mutexes or semaphores thereby never unknowing when which one of them executed first, second, so on. So, when program starts the execution out, all threads ramping to process and accomplish their tasks. Meanwhile, if they want to write to a common value, they have to wait each other(If you don't know its reason take a glance [at the link](https://stackoverflow.com/questions/34524/what-is-a-mutex).) – Soner from The Ottoman Empire May 26 '17 at 16:30
  • Mutex deadlock condition occur when a mutex is applied but then not "unlocked". This causes program execution to halt indefinitely. It can also be caused by poor application of mutexes or joins. when applying two or more mutexes to a section of code, we should be careful.If the first pthread_mutex_lock is applied and the second pthread_mutex_lock fails due to another thread applying a mutex, the first mutex may eventually lock all other threads from accessing data including the thread which holds the second mutex. – Soner from The Ottoman Empire May 26 '17 at 16:38
  • The threads may wait indefinitely for the resource to become free causing a deadlock. It is best to test and if failure occurs, free the resources and stall before retrying. En passant, OTOH, old hand @Brendan 's answer is another point. – Soner from The Ottoman Empire May 26 '17 at 16:38
0

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)
     }
}
Brendan
  • 35,656
  • 2
  • 39
  • 66