user997112
user997112

Reputation: 30605

Multithreading error within boost::shared_ptr code

I am using visual studio and I keep getting an exception on the following line of the boost::shared_ptr code:

void release() // nothrow
{
    if( BOOST_INTERLOCKED_DECREMENT( &use_count_ ) == 0 )
    {
        dispose();
        weak_release();
    }
}

I think it is multithreading because it is very random when it occurs.I'm struggling to get more details.

I share an unordered_map<std::string, boost::shared_ptr<MyClass>> amongst several threads. I think the error is due to different threads accessing the unordered_map at the same time (the threads don't access the same elements of the unordered_map).

MyClass contains an unordered_map and a set. The threads add numbers to these data structures. So if I had:

class MyClass{
public:
    void addToMap(double a, long b);
    void addToSet(double c);
private:
    unordered_map<double, long> a;
    set<double> b;
}

I do not have any locks in my code. Could someone please advise how I could potentially solve this problem (even if it means making the code slower)? Do I just need to insert mutexes inside each MyClass object? However it seems to be the boost::shared_ptr of the MyClass object which is causing the exception?

I do not pass any boost::shared_ptr objects by reference/pointer.

Upvotes: 0

Views: 249

Answers (1)

Casey
Casey

Reputation: 42544

operator[] modifies the container, and is therefore not safe to access concurrently. Your program has a data race. Generally, non-const member functions of standard library objects are not safe for simultaneous access. C++11 §23.2.2 lists some special exceptions for containers:

1 For purposes of avoiding data races (17.6.5.9), implementations shall consider the following functions to be const: begin, end, rbegin, rend, front, back, data, find, lower_bound, upper_bound, equal_range, at and, except in associative or unordered associative containers, operator[].

2 Notwithstanding (17.6.5.9), implementations are required to avoid data races when the contents of the contained object in different elements in the same sequence, excepting vector<bool>, are modified concurrently.

So for unordered_map, it's unsafe for multiple threads to simultaneously call operator[] - but it is safe for them to access distinct objects in the container simultaneously. Protecting the lookup of elements is sufficient, e.g.:

std::unique_lock<std::mutex> lk(some_mutex);
auto& foo = my_map["key"];
lk.unlock();
foo += 42;

Alternatively, if you only want to access existing elements in the map - and not add new default constructed elements - you can use find without external synchronization per the standard quote above.

Upvotes: 2

Related Questions