dimanche 16 décembre 2018

Implementing mutex using condition variables

I try to implement mutex using multiple condition variables inside, locked flag, global mutex for flag, and global mutexes for condition variables initialization (before initialization they are set to nullptr). I want to implement such mutex as exercise. Following code produces Segmentation fault, how to fix it?

#include <thread>
#include <vector>
#include <cassert>
#include <mutex>
#include <condition_variable>

using mutxCointainer = std::vector<std::shared_ptr<std::mutex>>;

class MutexOfConditions {
public:
    MutexOfConditions();

    void lock();

    void unlock();

private:
    const int static howMany = 10;
    std::shared_ptr<std::condition_variable> variables[howMany];
    unsigned long int counters[howMany];
    bool locked;

    std::mutex &getLockMutex();

    mutxCointainer &getVariablesMutexes();
};


MutexOfConditions::MutexOfConditions() {
    for (int i = 0; i < howMany; i++) {
        variables[i] = nullptr;
        counters[i] = 0;
    }
    locked = false;
}


mutxCointainer &MutexOfConditions::getVariablesMutexes() {
    static mutxCointainer mu;
    mu.reserve(howMany);
    for (int i = 0; i < howMany; i++) {
        mu.emplace_back(std::make_shared<std::mutex>());
    }
    return mu;
}

std::mutex &MutexOfConditions::getLockMutex() {
    static std::mutex mutex;
    return mutex;
}

void MutexOfConditions::lock() {
    getLockMutex().lock();
    if (locked) {
        getLockMutex().unlock();
        size_t MUT_INDEX = std::hash<std::thread::id>()(std::this_thread::get_id()) % howMany;
        getVariablesMutexes()[MUT_INDEX]->lock();
        if (variables[MUT_INDEX] == nullptr) {
            variables[MUT_INDEX] = std::make_shared<std::condition_variable>();
        }
        getVariablesMutexes()[MUT_INDEX]->unlock();
        std::unique_lock<std::mutex> lk(getLockMutex());
        variables[MUT_INDEX]->wait(lk, [this] { return !this->locked; });
        locked = true;
    } else {
        locked = true;
        getLockMutex().unlock();
    }
}

void MutexOfConditions::unlock() {
    getLockMutex().lock();
    locked = false;
    for (size_t i = 0; i < howMany; i++) {
        if (variables[i] != nullptr) {
            variables[i]->notify_one();
        }
    }
    getLockMutex().unlock();

}


const size_t THREADS_PER_USAGE = 5;
const int REPEATS = 5;

MutexOfConditions m1, m2, m3, m4, m5, m6, m7, m8;

int result = 0;

void usage1() {
    for (int i = 0; i < REPEATS; ++i) {
        m1.lock();
        m2.lock();
        m4.lock();
        m5.lock();
        m6.lock();
        m8.lock();

        ++result;

        m2.unlock();
        m4.unlock();
        m1.unlock();
        m5.unlock();
        m6.unlock();
        m8.unlock();
    }
}

void usage2() {
    for (int i = 0; i < REPEATS; ++i) {

        m3.lock();
        m2.lock();
        m4.lock();
        m6.lock();
        m5.lock();
        m7.lock();
        m8.lock();

        ++result;

        m3.unlock();
        m2.unlock();
        m4.unlock();
        m6.unlock();
        m5.unlock();
        m7.unlock();
        m8.unlock();
    }
}

int main() {
    std::vector<std::thread> threads;
    threads.reserve(THREADS_PER_USAGE * 2);

    for (size_t i = 0; i < THREADS_PER_USAGE; ++i) {
        threads.emplace_back(usage1);
        threads.emplace_back(usage2);
    }

    for (auto &thread : threads) {
        thread.join();
    }

    assert(result == 2 * THREADS_PER_USAGE * REPEATS);
}

Aucun commentaire:

Enregistrer un commentaire