-1

I have the function display.c :

/* DO NOT EDIT THIS FILE!!!  */

#include <stdio.h>
#include <unistd.h>
#include "display.h"

void display(char *str)
{
    char *p;

    for (p=str; *p; p++)
    {
        write(1, p, 1);
        usleep(100);
    }
}

and display.h is:

/* DO NOT EDIT THIS FILE!!!  */

#ifndef __CEID_OS_DISPLAY_H__
#define __CEID_OS_DISPLAY_H__
void display(char *);
#endif

My task is to use pthreads in order to have the following output:

abcd
abcd
abcd
..
..

Note that I must not edit the file display.c or the file display.c. I have to use mutexes in order to succeed the output that is shown above.

The following block of code is my closest attempt to finally reach the result I want:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <pthread.h>
#include "display.h"

pthread_t mythread1;
pthread_t mythread2;
pthread_mutex_t m1, m2;

void *ab(void *arg)
{
    pthread_mutex_lock(&m1);            
    display("ab");      
    pthread_mutex_unlock(&m1);
}    

void *cd(void *arg)
{    
    pthread_mutex_lock(&m1);       
    display("cd\n");            
    pthread_mutex_unlock(&m1);      
}

int main(int argc, char *argv[])
{
    pthread_mutex_init(&m1, NULL);
    pthread_mutex_init(&m2, NULL);  

    int i;

    for(i=0;i<10;i++)
    {     
        pthread_create(&mythread1, NULL, ab, NULL);         
        pthread_create(&mythread2, NULL, cd, NULL);    
    }

    pthread_join(mythread1, NULL);
    pthread_join(mythread2, NULL);   
    pthread_mutex_destroy(&m1);
    pthread_mutex_destroy(&m2);
    return EXIT_SUCCESS;    
}

The output of the code above is something like this:

abcd
abcd
abcd
abcd
ababcd
cd
abcd
abcd
abcd
abcd

As you can see "ab" and "cd\n" are never mixed but every time I run the code the output differs. I want to make sure that every time I run this code the output will be:

abcd
abcd 
abcd

for ten times.

I am really stuck with this since I can't find any solution from the things I already know.

Aris Kantas
  • 375
  • 1
  • 5
  • 15
  • @DavidSchwartz So there is no way to work with these mutexes? – Aris Kantas Jan 08 '16 at 19:54
  • Actually, now I'm confused. You've presented two programs, one using threads and one using processes. Which one are you trying to fix? (The first piece of code seems to have no relevance to your question whatsoever!) – David Schwartz Jan 08 '16 at 20:05
  • Process-shared mutexes would be appropriate if multiple processes were involved, but even in that case, one mutex cannot solve this problem by itself. In fact, however, the program in question (at the end) is a single program with multiple threads, so process-shared mutexes are moot. – John Bollinger Jan 08 '16 at 20:06
  • @JohnBollinger I think that first piece of code has no connection to his question whatsoever. – David Schwartz Jan 08 '16 at 20:07
  • @DavidSchwartz The first one is the given program by the teacher. The second one is my solution in order to synchronize the processes using threads and mutexes. – Aris Kantas Jan 08 '16 at 20:09
  • Your second one doesn't synchronize processes at all since there's only a single process. It's not clear what you're supposed to be doing. Are you supposed to be using threads or processes? That code will not solve the problem, nor is it the code you're trying to fix. So what purpose does that code serve? *Is that first piece of code the code you're supposed to fix?* – David Schwartz Jan 08 '16 at 20:10
  • @DavidSchwartz Yes the first piece of code is the code I am trying to fix. And it is mandatory to find a solution with threads. – Aris Kantas Jan 08 '16 at 20:22
  • @ArisKantas I don't understand how this can be right. To be clear, you are saying that: The first code does not work (it has no synchronization). The first code does not use threads (it uses processes). And you are trying to both fix it and change it to use threads? – David Schwartz Jan 08 '16 at 21:11
  • @DavidSchwartz It was my mistake that I showed you the first code. That's why I delete it on the last edit. All i want is to make sure the last block of code will work as it is said above. Sorry for the inconvenience. – Aris Kantas Jan 09 '16 at 01:30

3 Answers3

2

A mutex cannot (by itself) solve your problem. It can prevent your two threads from running concurrently, but it cannot force them to take turns.

You can do this with a condition variable in addition to the mutex, or with a pair of semaphores. Either way, the key is to maintain at all times a sense of which thread's turn it is.

Myself, I think the semaphore approach is easier to understand and code. Each semaphore is primarily associated with a different thread. That thread must lock the semaphore to proceed. When it finishes one iteration it unlocks the other semaphore to allow the other thread to proceed, and loops back to try to lock its own semaphore again (which it cannot yet do). The other thread works the same way, but with the semaphore roles reversed. Roughly, that would be:

sem_t sem1;
sem_t sem2;

// ...

void *thread1_do(void *arg) {
    int result;

    do {
        result = sem_wait(&sem1);
        // do something
        result = sem_post(&sem2);
    } while (!done);
}

void *thread2_do(void *arg) {
    int result;

    do {
        result = sem_wait(&sem2);
        // do something else
        result = sem_post(&sem1);
    } while (!done);
}

Semaphore initialization, error checking, etc. omitted for brevity.

Updated to add:

Since you now add that you must use mutexes (presumably in a non-trivial way) the next best way to go is to introduce a condition variable (to be used together with the mutex) and an ordinary shared variable to track which thread's turn it is. Each thread then waits on the condition variable to obtain the mutex, under protection of the mutex checks the shared variable to see whether it is its turn, and if so, proceeds. Roughly, that would be:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int whose_turn = 1;

// ...

void *thread1_do(void *arg) {
    int result;

    result = pthread_mutex_lock(&mutex);
    while (1) {
        if (whose_turn == 1) {
            // do something
            whose_turn = 2;  // it is thread 2's turn next
        }
        // break from the loop if finished
        result = pthread_cond_broadcast(&cond);
        result = pthread_cond_wait(&cond, &mutex); 
    }
    result = pthread_mutex_unlock(&mutex);
}

void *thread1_do(void *arg) {
    int result;

    result = pthread_mutex_lock(&mutex);
    while (1) {
        if (whose_turn == 2) {
            // do something else
            whose_turn = 1;  // it is thread 1's turn next
        }
        // break from the loop if finished
        result = pthread_cond_broadcast(&cond);
        result = pthread_cond_wait(&cond, &mutex); 
    }
    result = pthread_mutex_unlock(&mutex);
}

Error checking is again omitted for brevity.

Note in particular that when a thread waits on a condition variable, it releases the associated mutex. It reaquires the mutex before returning from the wait. Note also that each checks at each iteration whether it is its turn to proceed. This is necessary because spurious wakeups from waiting on a condition variable are possible.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

You can use a conditional variable to take turns between threads:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int turn = 0;

void *ab(void *arg)
{
    pthread_mutex_lock(&m1);
    while (turn != 0)
       pthread_cond_wait(&cond, &m1);

    display("ab");
    turn = 1;
    pthread_mutex_unlock(&m1);
    pthread_cond_signal(&cond);
}

void *cd(void *arg)
{
    pthread_mutex_lock(&m1);
    while (turn != 1)
       pthread_cond_wait(&cond, &m1);

    display("cd\n");
    turn = 0;
    pthread_mutex_unlock(&m1);
    pthread_cond_signal(&cond);
}

Another problem is, you are joining with the last two pair of threads created in main() thread, which are not necessarily the ones get executed as last. If threads created early are not completed, then you are destroying the mutex m1 while it might be in use and exiting whole process.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Yes that finally worked. I am very new to this and you made me understand better about contition variables. – Aris Kantas Jan 08 '16 at 20:46
  • Signaling the condition variable while not holding the relevant mutex causes a race condition. – EOF Jan 08 '16 at 22:43
  • @EOF Care to cite POSIX that says so? (because it isn't). – P.P Jan 08 '16 at 22:53
  • http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html :`[...]That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_signal() or pthread_cond_broadcast() in that thread behaves as if it were issued after the about-to-block thread has blocked.` Note that the behavior you expect is predicated on the signaling thread holding the mutex. Why else would you need the mutex at all? – EOF Jan 08 '16 at 23:17
  • @EOF How is relevant to your previous comment? Read: http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html `The pthread_cond_broadcast() or pthread_cond_signal() functions may be called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits`. – P.P Jan 08 '16 at 23:36
  • No such citation is found on the linked page. – EOF Jan 09 '16 at 00:11
  • @EOF http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html Signaling cv first or releasing a mutex first is an application's choice. POSIX doesn't restrict it one or other. – P.P Jan 09 '16 at 00:12
  • Your silent edit of the citation could easily be construed as malicious. `[...]; however, if predictable scheduling behavior is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal().` – EOF Jan 09 '16 at 11:13
  • @EOF I didn't edit anything (answer or comments). What are you talking about? – P.P Jan 09 '16 at 11:29
  • Read: http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex#comment9223783_4567919 and all the answers and comments before accusing me of being malicious. – P.P Jan 09 '16 at 11:44
  • @EOF And this: https://groups.google.com/forum/?hl=ky#!msg/comp.programming.threads/wEUgPq541v8/ZByyyS8acqMJ A race condition implies a semantic/programming error in which the correctness depends on the (expected) order execution. Butenhof explains why the citation you quoted was added (mainly to satisfy some to get the standard moving). – P.P Jan 09 '16 at 12:16
  • Soon after this answer was posted, I posted and then soon deleted my own comment about a possible race condition. Regardless of whether in this case there actually is a race condition or other flaw inherent in unlocking the mutex first, the fact that knowledgeable and experienced people can argue about it is a reason in itself to swap the broadcast with the mutex unlock. It would be much easier to reason about the resulting code, and there would be little room for uncertainty about whether it is correct. – John Bollinger Jan 10 '16 at 21:42
  • Just in case anybody stumbles upon this, I should probably note that I was wrong in this discussion (and belligerent about it too). Unlike in Java, pthreads condition variables *can* be signalled while not holding the mutex, provided the signaller *acquired* the mutex (and particularly held it while changing the shared state the blocked thread uses for its predicate), whether or not it has *released* the mutex since. Unfortunately, I cannot un-downvote because the downvote is too old and the answer has not been edited since. – EOF Nov 04 '18 at 12:45
-1

Consider this approach to the issue:

for(i=0;i<10;i++)
{     
    ab();
    cd();
}

This achieves your goals completely, given the shown code. The problem with your example is that you effectively prevent any synchronization and that seems to be your goal even!

Assuming that before the output, you actually want to do something useful which takes CPU time, the answer is that you will have to change the code of display(), which is simply not suitable for parallelization. Proper concurrent code is designed to work independent of other code, in particular it shouldn't compete for resources (locks) with other calls and it shouldn't rely on the order it finishes.

In summary, you can't learn much from this, it's a bad example. In order to improve the code (the one you don't want to change, but that's your problem), consider that the resource the different threads compete for is stdout. If they each wrote to their own buffer, you could create the threads, wait for them to finish and only then reorder their results for output.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • It is very clear that I must not edit the display function. – Aris Kantas Jan 08 '16 at 20:25
  • Yes, you wrote that. However, if you insist on using code that sereverly sucks (as far as multithreading is concerned), then this code simply can't be parallelized! If for some deranged fascination with threads you want to use them, you could call `ab()` via in a thread each, which you immediately join again. Then the same for `bc()`. That's complete and utter nonsense though! – Ulrich Eckhardt Jan 08 '16 at 20:30
  • Well this is an exercise for my university and it is mandatory to use display.c – Aris Kantas Jan 08 '16 at 20:33