Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map of mutex c++11

I need to make a thread-safe map, where I mean that each value must be independently mutexed. For example, I need to be able to get map["abc"] and map["vf"] at the same time from 2 different threads.

My idea is to make two maps: one for data and one for mutex for every key:

class cache
{
private:
....

    std::map<std::string, std::string> mainCache;
    std::map<std::string, std::unique_ptr<std::mutex> > mutexCache;
    std::mutex gMutex;
.....
public:
    std::string get(std::string key);

};
std::string cache::get(std::string key){
    std::mutex *m;
    gMutex.lock();
    if (mutexCache.count(key) == 0){
        mutexCache.insert(new std::unique_ptr<std::mutex>);
    }
    m = mutexCache[key];
    gMutex.unlock();
}

I find that I can't create map from string to mutex, because there is no copy constructor in std::mutex and I must use std::unique_ptr; but when I compile this I get:

/home/user/test/cache.cpp:7: error: no matching function for call to 'std::map<std::basic_string<char>, std::unique_ptr<std::mutex> >::insert(std::unique_ptr<std::mutex>*)'
         mutexCache.insert(new std::unique_ptr<std::mutex>);
                                                          ^

How do I solve this problem?

like image 781
Mike Minaev Avatar asked Aug 28 '14 08:08

Mike Minaev


2 Answers

TL;DR: just use operator [] like std::map<std::string, std::mutex> map; map[filename];

Why do you need to use an std::unique_ptr in the first place?

I had the same problem when I had to create an std::map of std::mutex objects. The issue is that std::mutex is neither copyable nor movable, so I needed to construct it "in place".

I couldn't just use emplace because it doesn't work directly for default-constructed values. There is an option to use std::piecewise_construct like that:

map.emplace(std::piecewise_construct, std::make_tuple(key), std::make_tuple());

but it's IMO complicated and less readable.

My solution is much simpler - just use the operator[] - it will create the value using its default constructor and return a reference to it. Or it will just find and return a reference to the already existing item without creating a new one.

std::map<std::string, std::mutex> map;

std::mutex& GetMutexForFile(const std::string& filename)
{
    return map[filename]; // constructs it inside the map if doesn't exist
}
like image 34
Roman Kruglov Avatar answered Sep 22 '22 12:09

Roman Kruglov


Replace mutexCache.insert(new std::unique_ptr<std::mutex>) with:

mutexCache.emplace(key, new std::mutex);

In C++14, you should say:

mutexCache.emplace(key, std::make_unique<std::mutex>());

The overall code is very noisy and inelegant, though. It should probably look like this:

std::string cache::get(std::string key)
{
    std::mutex * inner_mutex;

    {
        std::lock_guard<std::mutex> g_lk(gMutex);

        auto it = mutexCache.find(key);
        if (it == mutexCache.end())
        {
            it = mutexCache.emplace(key, std::make_unique<std::mutex>()).first;
        }
        inner_mutex = it->second.get();
    }

    {
        std::lock_guard<std::mutex> c_lk(*inner_mutex);
        return mainCache[key];
    }
}
like image 67
Kerrek SB Avatar answered Sep 22 '22 12:09

Kerrek SB