Caduchon Caduchon - 1 month ago 9
C++ Question

Safe use of a reference in a map in multi-threaded context

The context

I have a class (let's say

Foo
) managing some centralized ressources as an history in a static map, with an accessor to read them and a function to add new data (there is no way to remove a key) :

class Foo
{
private:
static std::map<std::string,MyDataStructure> data;
public:
static const MyDataStructure& getData(const std::string& key)
{
assert(Foo::data.count(key) > 0); // Must exist
return Foo::data[key];
}
static void addData(const std::string& key, const MyDataStructure& d)
{
assert(Foo::data.count(key) == 0); // Can not already exist
Foo::data[key] = d;
}
};


In order to avoid concurrency problems, I added a mutex I manage like that :

class Foo
{
private:
static std::map<std::string,MyDataStructure> data;
static boost::mutex mutex_data;
public:
static const MyDataStructure& getData(const std::string& key)
{
boost::mutex::scoped_lock lock(Foo::mutex_data);
assert(Foo::data.count(key) > 0); // Must exist
return Foo::data[key];
}
static void addData(const std::string& key, const MyDataStructure& d)
{
boost::mutex::scoped_lock lock(Foo::mutex_data);
assert(Foo::data.count(key) == 0); // Can not already exist
Foo::data[key] = d;
}
};


My questions


  1. My first question is about the reference to
    Foo::data
    returned by
    getData
    : this reference is used out of the scope of the mutex, then is it possible to have a problem with that ? Is it possible to loose the reference due to another access adding data ? In brief : is the reference always the same once in the map ?

  2. If yes, is the
    assert
    required in
    addData
    ? Can the reference change if I change data linked to an existing key in the map ?

  3. Is the lock required in
    getData
    ? I think maybe not if
    std::map
    is already multi-thread safe.


Answer
  1. Is is possible to loose the reference due to another access adding data ? In brief : is the reference always the same once in the map?

See Lifetime of references in STD collections

"for std::map, the references are valid as long as you don't clear the map, or erase the specific referenced element; inserting or erasing other elements is fine."

  1. Can the reference change if I change data linked to an existing key in the map?

Pursuant to the rule above, no...modifying a reference already in the map is just tweaking the bits at that same address. (As long as you don't implement modification via removal of the key and then re-adding that key again.)

  1. Is the lock required in getData ? I think maybe not if std::map is already multi-thread safe.

See C++11 STL containers and thread safety

So the lock is required, because the "internal wiring" of the map structure may be in flux during addData()'s insertion...that wiring might be tripped on during the lookup traversal for getData() without a guard.

However, you can be reading or writing one of the already-present MyDataStructure references during an add or removal, if you have the reference in your hand. It's just that process of navigating from map-to-reference that needs to be sure no one is writing during the navigation.