0

I have problem with readers-writers problem. I want to write writers favor solution using mutex. So far i have written this

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <memory.h>
#include <stdbool.h>
#include <stdint.h>
#include<unistd.h>

int NO_READERS;
int NO_WRITERS;
int NO_READERS_READING = 0;     // How many readers need shared resources
int NO_WRITERS_WRITING = 0;     // How many writers need shared resources

pthread_mutex_t resourceMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t tryResourceMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t readerMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t writerMutex = PTHREAD_MUTEX_INITIALIZER;

void *readerJob(void *arg) {
    int *id = (int*)arg;
    while (1) {
        pthread_mutex_lock(&tryResourceMutex); // Indicate reader is trying to enter
        pthread_mutex_lock(&readerMutex);
        NO_READERS_READING++; // Indicate that you are needing the shared resource (one more reader)
        if (NO_READERS_READING == 1) {
            pthread_mutex_lock(&resourceMutex);
        }
        pthread_mutex_unlock(&readerMutex);
        pthread_mutex_unlock(&tryResourceMutex);
         printf("READER ID %d WALKED IN \n",*id);
        printf("ReaderQ: %d , WriterQ: %d [in: R:%d W:%d]\n",
                NO_READERS - NO_READERS_READING,
                NO_WRITERS - NO_WRITERS_WRITING,
                NO_READERS_READING,
                NO_WRITERS_WRITING);
        sleep(1);
        pthread_mutex_lock(&readerMutex);
        NO_READERS_READING--;
        if (NO_READERS_READING == 0) { // Check if you are the last reader
            pthread_mutex_unlock(&resourceMutex);
        }
        pthread_mutex_unlock(&readerMutex);
    }
    return 0;
}

void *writerJob(void *arg) {
    int *id = (int*)arg;
    while (1) {
        pthread_mutex_lock(&writerMutex);
        NO_WRITERS_WRITING++;
        if (NO_WRITERS_WRITING == 1) {
            pthread_mutex_lock(&tryResourceMutex); // If there are no other writers lock the readers out
        }
        pthread_mutex_unlock(&writerMutex);

        pthread_mutex_lock(&resourceMutex);
        printf("WRITER ID %d WALKED IN \n",*id);
        printf("ReaderQ: %d , WriterQ: %d [in: R:%d W:%d]\n",
                NO_READERS - NO_READERS_READING,
                NO_WRITERS - NO_WRITERS_WRITING,
                NO_READERS_READING,
                NO_WRITERS_WRITING);
        sleep(1);
        pthread_mutex_unlock(&resourceMutex);

        pthread_mutex_lock(&writerMutex);
        NO_WRITERS_WRITING--;
        if (NO_WRITERS_WRITING == 0) {
            pthread_mutex_unlock(&tryResourceMutex); // If there are no writers left unlock the readers
        }
        pthread_mutex_unlock(&writerMutex);
    }
    return 0;
}

int main(int argc, char *argv[]) {
    NO_READERS = atoi(argv[1]);
    NO_WRITERS = atoi(argv[2]);

    // Initialize arrays of threads IDs
    pthread_t *readersThreadsIds = malloc(NO_READERS * sizeof(pthread_t));
    pthread_t *writersThreadsIds = malloc(NO_READERS * sizeof(pthread_t));

    // Initialize shared memory (array) with random numbers

    // Create readers threads
    for (int i = 0; i < NO_READERS; ++i) {
        int* id = (int*)(malloc(sizeof(int)));
        *id = i;
        pthread_create(&readersThreadsIds[i], NULL, readerJob,(void*)id);
    }
    // Create writers threads
    for (int i = 0; i < NO_WRITERS; ++i) {
        int* id = (int*)(malloc(sizeof(int)));
        *id = i;
        pthread_create(&writersThreadsIds[i], NULL, writerJob, (void*)id);

    }

    // Wait for readers to finish
    for (int i = 0; i < NO_READERS; ++i) {
        pthread_join(readersThreadsIds[i], NULL);
        }
    // Wait for writers to finish
    for (int i = 0; i < NO_WRITERS; ++i) {
        pthread_join(writersThreadsIds[i], NULL);
    }

    free(readersThreadsIds);
    free(writersThreadsIds);
    pthread_mutex_destroy(&resourceMutex);
    pthread_mutex_destroy(&tryResourceMutex);
    pthread_mutex_destroy(&readerMutex);
    pthread_mutex_destroy(&writerMutex);
    return 0;
}

And I'm not sure if this should be working like this. Can anyone check this for me? I want to have information about which reader or writer is going in or out. It seems like it stuck in some point but I don't know why.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • At the end, you seem to be saying that the program does not work as you expect. It would be appropriate to present the program output or other information that leads you to draw that conclusion, and explain your analysis of it. – John Bollinger Jun 11 '19 at 16:29
  • At the begining all readers are coming in, then also writers which shouldn't be possible at the same time. Then all readers are gone but there are more than one writer at the same time in library. The best idea is to run it and see for yourself. – kubek_tysionc Jun 11 '19 at 16:36

2 Answers2

1

It seems to do what you want, that is give preference to the writers. Because your threads loop acquiring and releasing the lock; if you have more than one writer, the writers will take turns passing it between themselves and starve the readers. That is, every time one releases the resourceMutex, there is another writer waiting on it, so NO_WRITERS_WRITING will never hit zero.

To see it operating as intended, add a delay at the top of the while loop of each thread:

usleep((rand() % 10000) * 10000);

That will permit the readers to periodically get access, whenever all the writers are in the usleep().

mevets
  • 10,070
  • 1
  • 21
  • 33
0

At the begining all readers are coming in,

By "coming in", I take you to mean executing the printf() calls in the readerJob loop. It's not surprising that the readers all come in first, because you start them first, and in the likely event that the first reader thread to attempt to lock tryResourceMutex does so before any writer thread does, it will then lock resourceMutex(), too, preventing any writer from "coming in". But that does not prevent writers from incrementing NO_WRITERS_WRITING. And it also does not preventing one of them from locking tryResourceMutex and holding it locked.

The sleep() call in the reader will then (probably) cause resourceMutex to be held continuously long enough that all the readers come in before any of the writers do, since each writer needs to acquire resourceMutex to come in.

then also writers which shouldn't be possible at the same time.

I don't see that in my tests. But I do see what I already described: the writer count increases from zero, even though they are prevented from coming in while any readers are inside. In effect, the name of your variable NO_WRITERS_WRITING is inconsistent with your actual usage -- indicates how many writers are writing or waiting to write.

When the readers leave they are blocked from reentering right away because one of the writers holds tryResourceMutex. Eventually, then, the last reader will exit and release the resourceMutex. This will allow the writers to proceed, one at a time, but with the sleep() call positioned where it is in the writer loop, it is extremely unlikely that the number of writers will ever fall to zero to allow any of the readers to re-enter. If it did, however, then very likely the same cycle would repeat: all of the readers would enter, once, while all the writers queue up.

Then all readers are gone but there are more than one writer at the same time in library.

Again, no. Only one writer is inside at a time, but the others are queued most of the time, so NO_WRITERS_WRITING will almost always be equal to NO_WRITERS.

Bottom line, then: you have confused yourself. You are using variable NO_WRITERS_WRITING primarily to represent the number of writers that are ready to write, but your messaging uses it as if it were the number actually writing. The same does not apply to NO_READERS_READING because once a thread acquires the mutex needed to modify that variable, nothing else prevents it from proceeding on into the room.

One more thing: to make the simulation interesting -- i.e. to keep the writers from taking permanent control -- you should implement a delay, preferably a random one, after each thread leaves the room, before it tries to reenter. And the delay for writers should probably be substantially longer than the delay for readers.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Yeah, I see what I did wrong with NO_WRITERS_WRITING I added one more variable to count writers. But I still can't simulate it right. Maybe because I don't know how should correct outcome look like. I have problem with placing those sleeps. https://pastebin.com/snB5XhT8 Is it correct now? – kubek_tysionc Jun 11 '19 at 19:00
  • @kubek_tysionc, if the writer `sleep()`s between incrementing `NO_WRITERS_WRITING` and decrementing it again, especially if it does so while holding `resourceMutex`, then that simulates the writer taking some time to complete its task. That's ok, but if the readers are ever to run then there must be times when no writers are working. That would be best implemented by a second `sleep` -- a longer one, preferrably random one -- either at the very beginning or at the very end of the writer loop. – John Bollinger Jun 11 '19 at 19:14