3
\$\begingroup\$

This is educational purpose "simulation" of multiple readers / single writer server.

No actual work is done, except the synchronization. In current realization, readers have priority over writers.

#include <thread>
#include <mutex>
#include <condition_variable>
#include <shared_mutex>

#include <vector>
#include <cstdio>

#include <chrono>

class Server{
public:
    void read(int const n){
        std::unique_lock<std::mutex> lock(mutex_);

        ++rCount;

        while(status_ == Status::WRITE)
            rQueue_.wait(lock);

        status_ = Status::READ;

        lock.unlock();

        emulate_work_(n);

        lock.lock();

        --rCount;

        if (rCount == 0){
            status_ = Status::IDLE;
            wQueue_.notify_one();
        }
    }

    void write(int const n){
        std::unique_lock<std::mutex> lock(mutex_);

        while(status_ != Status::IDLE)
            wQueue_.wait(lock);

        status_ = Status::WRITE;

        emulate_work_(n);

        status_ = Status::IDLE;

        if (rCount)
            rQueue_.notify_all();
        else
            wQueue_.notify_one();
    }

private:
    void emulate_work_(int const n) const{
        {
            std::lock_guard<std::mutex> lock(mutex_stdout_);

            if (status_ == Status::READ)
                printf("Thread %3d READ  | Readers %3zu\n", n, rCount);
            else
                printf("Thread %3d WRITE |\n", n);

        }

        // working...

        using namespace std::chrono_literals;

        std::this_thread::sleep_for(2ms);
    }

private:
    enum class Status : char{
        IDLE    = '.',
        READ    = 'R',
        WRITE   = 'W'
    };

private:
    mutable
    std::mutex      mutex_stdout_;

    std::mutex      mutex_;

    std::condition_variable rQueue_;
    std::condition_variable wQueue_;

    size_t rCount = 0;
//  size_t wCount = 0;

    Status status_ = Status::IDLE;
};

void reader(int const n, Server &server){
    for(int i = 0; i < 100; ++i){
        server.read(n);

        using namespace std::chrono_literals;

        std::this_thread::sleep_for(3ms);
    }
}

void writter(int n, Server &server){
    for(int i = 0; i < 20; ++i){
        server.write(n);

        using namespace std::chrono_literals;

        std::this_thread::sleep_for(1ms);
    }
}

int main(){
    std::vector<std::thread> v;

    Server server;

    for (int n = 0; n < 5; ++n) {
        v.emplace_back(writter, n, std::ref(server));
    }

    for (int n = 0; n < 5; ++n) {
        v.emplace_back(reader, n, std::ref(server));
    }

    for (auto& t : v) {
        t.join();
    }
}
\$\endgroup\$
  • \$\begingroup\$ Your title doesn't really match your code - it looks like you have multiple writers? \$\endgroup\$ – Nim Jan 17 '17 at 15:24
  • \$\begingroup\$ yes. but only one write at single point of time and in same time all readers are blocked \$\endgroup\$ – Nick Jan 17 '17 at 15:35
  • \$\begingroup\$ what is the point of the two condition variables? you are only waiting on the wQueue_ condition.. \$\endgroup\$ – Nim Jan 17 '17 at 16:02
  • \$\begingroup\$ you just found an error. thanks. Code edited. \$\endgroup\$ – Nick Jan 17 '17 at 16:44
  • \$\begingroup\$ @Nick Do you have particular concerns what should be reviewed in that code? The timing constraints seem unrealistic to simulate a real world scenario. Also why are you using printf() in c++ code? \$\endgroup\$ – πάντα ῥεῖ Jan 17 '17 at 20:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.