Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to solve producer-consumer problem using atomic_compare_exchange_weak?

I am implementing the ‘producer-consumer problem’ in C, which solves the synchronization problem of redundant production, redundant consumption, and consuming unproduced items when multi-threading.

bool expected = false; 
while (!atomic_compare_exchange_weak(&lock, &expected, true))
    expected = false;

In order to prevent duplicate access of the process, the atomic_compare_exchange_weak was used.

I know that the atomic_compare_exchange_weak can block access of another process when one process enters critical section. However, duplicate access of the process continues to occur. How can I solve this problem? Below is my producer and consumer code.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

producer code

void *producer(void *arg) {
    int i = *(int *)arg;
    int item;
    
    while(alive) {

        while(counter >= BUFSIZE);
        
        expected_p = false;
        while (!atomic_compare_exchange_weak(&lock_p, &expected_p, true))
            expected_p = false;
        
        item = next_item++;
        
        buffer[in] = item;
        in = (in + 1) % BUFSIZE;
        counter++;  
            
        if (task_log[item][0] == -1) {
            task_log[item][0] = i;
            produced++;
            lock_p = false;
        }
        else {
            printf("ERROR\n");
            continue;
        } 
        printf("<P%d,%d>", i, item); 
    }
    pthread_exit(NULL); 
}

consumer code

void *consumer(void *arg)
{
    int i = *(int *)arg;
    int item;

    while (alive) {

        while (counter <= 0);

        expected_c = false;
        while (!atomic_compare_exchange_weak(&lock_c, &expected_c, true)) 
            expected_c = false;
        
        item = buffer[out];
        out = (out + 1) % BUFSIZE;    
        counter--; 
        
        if (task_log[item][0] == -1) {
            printf("ERROR\n");
            continue;
        }
        else if (task_log[item][1] == -1) {
            task_log[item][1] = i;
            consumed++;
            lock_c = false; 
        }
        else {
            printf("ERROR\n");
            continue;
        }
        printf("<C%d,%d>\n", i, item);   
    }
    pthread_exit(NULL);
}

global value

int buffer[BUFSIZE];
int in = 0;
int out = 0;
int counter = 0;
int next_item = 0;

bool expected_p = false;
bool expected_c = false;
atomic_bool lock_p = false;
atomic_bool lock_c = false;

>Solution :

Just use a pthread_cond and pthread_mutex instead of trying to do something crazy with atomics. The code below may have more overhead using a real lock that an atomic primitive, but it’s far safer and more maintainable. Further, the condition variable in place of your spin loops will increase performance of both threads dramatically – overcoming the overhead of using a real lock.

Declare a mutex and a pair of condition variables:

pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_can_write, cand_can_read;

Then start both threads and modify as follows:

void *producer(void *arg) {

    int i = *(int *)arg;
    int item;
    
    while(alive) {

        pthread_mutex_lock(&mut);  // TAKE THE LOCK

        // wait for counter to go below BUFSIZE
        while (counter >= BUFSIZE) {
            // atomically release lock and wait for consumer thread to signal
            pthread_cond_wait(&cond_can_write, &mut);

           // when pthread_cond_wait returns, this thread has reacquired the lock
        }

        // DO YOUR PRODUCE LOGIC HERE        

        pthread_mutex_unlock(&mut); // EXIT THE LOCK

        // signal to consumer that it can read
        pthread_cond_broadcast(&cond_can_read);
    }
    pthread_exit(NULL); 
}

void *consumer(void *arg)
{
    int i = *(int *)arg;
    int item;

    while (alive) {

        pthread_mutex_lock(&mut);   // TAKE THE LOCK

        // Wait for the counter to go above 0
        while (counter <= 0) {
            // atomically release lock and wait for producer thread to signal
            pthread_cond_wait(&cond_can_read, &mut);

            // when pthread_cond_wait returns, this thread has reacquired the lock
        }

        // DO YOUR CONSUME LOGIC HERE

        pthread_mutex_unlock(&mut);  // EXIT THE LOCK

        // signal to producer that it can write again
        pthread_cond_broadcast(&cond_write);

    }
    pthread_exit(NULL);
}

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading