worknovel
worknovel

Reputation: 73

Reader Writer Problem With Writer Priority Problem

I came across this problem as I am learning more about operating systems. In my code, I've tried making the reader having priority and it worked, so next I modified it a bit to make the writer have the priority. When I ran the code, the output was exactly the same and it seemed like the writer did not have the priority. Here is the code with comments. I am not sure what I've done wrong, since I modified a lot of the code but the output remains the same if I did not change it at all.

#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>

/*
This program provides a possible solution for first readers writers problem using mutex and semaphore.
I have used 10 readers and 5 producers to demonstrate the solution. You can always play with these values.
*/

// Semaphore initialization for writer and reader 
sem_t wrt;
sem_t rd;

// Mutex 1 blocks other readers, mutex 2 blocks other writers
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;

// Value the writer is changing, we are simply multiplying this value by 2
int cnt = 2;

int numreader = 0;
int numwriter = 0;

void *writer(void *wno)
{   
    pthread_mutex_lock(&mutex2);
    numwriter++;
    if(numwriter == 1){
        sem_wait(&rd);      
    }
    pthread_mutex_unlock(&mutex2);
    sem_wait(&wrt); 

    // Writing Section
    cnt = cnt*2;
    printf("Writer %d modified cnt to %d\n",(*((int *)wno)),cnt);

    sem_post(&wrt);
    pthread_mutex_lock(&mutex2);
    numwriter--;
    if(numwriter == 0){
        sem_post(&rd);
    }
    pthread_mutex_unlock(&mutex2); 

}
void *reader(void *rno)
{   
    sem_wait(&rd);
    pthread_mutex_lock(&mutex1);
    numreader++;
    if(numreader == 1){
        sem_wait(&wrt);
    }
    pthread_mutex_unlock(&mutex1);
    sem_post(&rd); 

    // Reading Section
    printf("Reader %d: read cnt as %d\n",*((int *)rno),cnt);

    
    pthread_mutex_lock(&mutex1);
    numreader--;
    if(numreader == 0){
        sem_post(&wrt);
    }
    pthread_mutex_unlock(&mutex1); 
}

int main()
{   

    pthread_t read[10],write[5];
    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);
    sem_init(&wrt,0,1);
    sem_init(&rd,0,1);

    int a[10] = {1,2,3,4,5,6,7,8,9,10}; //Just used for numbering the writer and reader

    for(int i = 0; i < 5; i++) {
        pthread_create(&write[i], NULL, (void *)writer, (void *)&a[i]);
    }

    for(int i = 0; i < 10; i++) {
        pthread_create(&read[i], NULL, (void *)reader, (void *)&a[i]);
    }

    for(int i = 0; i < 5; i++) {
        pthread_join(write[i], NULL);
    }

    for(int i = 0; i < 10; i++) {
        pthread_join(read[i], NULL);
    }

    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    sem_destroy(&wrt);
    sem_destroy(&rd);

    return 0;
    
}

Output (for both is the same. I think if writer had priority it will change first, then will be read): enter image description here

Upvotes: 0

Views: 3123

Answers (2)

Vacation Due 20000
Vacation Due 20000

Reputation: 37

Sorry to put this as an answer. (I can't comment.) your programs with a little bit of changes works fine for me.

#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>

// Semaphore initialization for writer and reader 
sem_t wrt;
sem_t rd;

// Mutex 1 blocks other readers, mutex 2 blocks other writers
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;

// Value the writer is changing, we are simply multiplying this value by 2
int cnt = 2;

int numreader = 0;
int numwriter = 0;

void *writer(void *wno)
{   
    pthread_mutex_lock(&mutex2);
    numwriter++;
    if (numwriter == 1) {
        sem_wait(&rd);      
    }
    pthread_mutex_unlock(&mutex2);
    sem_wait(&wrt); 

    // Writing Section
    cnt = cnt * 2;
    printf("Writer %d modified cnt to %d\n", *((int *)wno), cnt);

    sem_post(&wrt);
    pthread_mutex_lock(&mutex2);
    numwriter--;
    if (numwriter == 0) {
        sem_post(&rd);
    }
    pthread_mutex_unlock(&mutex2); 

    return NULL; // Correctly return a void pointer
}

void *reader(void *rno)
{   
    sem_wait(&rd);
    pthread_mutex_lock(&mutex1);
    numreader++;
    if (numreader == 1) {
        sem_wait(&wrt);
    }
    pthread_mutex_unlock(&mutex1);
    sem_post(&rd); 

    // Reading Section
    printf("Reader %d: read cnt as %d\n", *((int *)rno), cnt);

    pthread_mutex_lock(&mutex1);
    numreader--;
    if (numreader == 0) {
        sem_post(&wrt);
    }
    pthread_mutex_unlock(&mutex1); 

    return NULL; // Correctly return a void pointer
}

int main()
{   
    pthread_t read[10], write[5];
    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);
    sem_init(&wrt, 0, 1);
    sem_init(&rd, 0, 1);

    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // Just used for numbering the writer and reader

    for (int i = 0; i < 5; i++) {
        pthread_create(&write[i], NULL, writer, &a[i]); // No explicit casting
    }

    for (int i = 0; i < 10; i++) {
        pthread_create(&read[i], NULL, reader, &a[i]); // No explicit casting
    }

    for (int i = 0; i < 5; i++) {
        pthread_join(write[i], NULL);
    }

    for (int i = 0; i < 10; i++) {
        pthread_join(read[i], NULL);
    }

    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    sem_destroy(&wrt);
    sem_destroy(&rd);

    return 0;
}

here is the proof:

proof

Upvotes: 0

Davislor
Davislor

Reputation: 15154

Alternative Semantics

Much of what you want to do can probably be accomplished with less overhead. For example, in the classic reader-writer problem, readers shouldn’t need to block other readers.

You might be able to replace the reader-writer pattern with a publisher-consumer pattern that manages pointers to blocks of data with acquire-consume memory ordering. You only need locking at all if one thread needs to update the same block of memory after it was originally written.

POSIX and Linux have an implementation of reader-writer locks in the system library, which were designed to avoid starvation. This is most likely the high-level construct you want.

If you still want to implement your own, one implementation would use a count of current readers, a count of pending writers and a flag that indicates whether a write is in progress. It packs all these values into an atomic bitfield that it updates with a compare-and-swap.

Reader threads would retrieve the value, check whether there are any starving writers waiting, and if not, increment the count of readers. If there are writers, it backs off (perhaps spinning and yielding the CPU, perhaps sleeping on a condition variable). If there is a write in progress, it waits for that to complete. If it sees only other reads in progress, it goes ahead.

Writer threads would check if there are any reads or writes in progress. If so, they increment the count of waiting writers, and wait. If not, they set the write-in-progress bit and proceed.

Packing all these fields into the same atomic bitfield guarantees that no thread will think it’s safe to use the buffer while another thread thinks it’s safe to write: if two threads try to update the state at the same time, one will always fail.

If You Stick With Semaphores

You can still have reader threads check sem_getvalue() on the writer semaphore, and back off if they see any starved writers are waiting. One method would be to wait on a condition variable that threads signal when they are done with the buffer. A reader that sees that it holds the mutex while writers are waiting can try to wake up one writer thread and go back to sleep, and a reader that sees only other readers are waiting can wake up a reader, which will wake up the next reader, and so on.

Upvotes: 0

Related Questions