Question

I have the following code:

shared_ptr<A> a;
B b(a);
a.reset(new A());

while in class B I create a new thread and wait for a to be "ready":

B(shared_ptr<A> a) {
    _a = a;
    //create thred here
}

//...

//in thread:
while (_this->_a == NULL) {}

The problem is that even after the line a.reset(new A()); is executed, the thread started in B still thinks that _this->_a is empty and never leaves the while loop!

I tried to use a = make_shared(new A()); or a = shared_ptr(new A()); - the effect still the same. I also tried to pass a as const reference - same thing.

Most interesting thing is that when I use a plain pointer and pass a reference to that pointer to class B, everything works fine:

A* a;
B b(a);
a = new A();

//...

B(A*& a) {
    _a = a;
    //create thred here
}

//in thread:
while (_this->_a == NULL) {}
//In this case the loop is finished

What do I miss here?

UPDATE (01.05.2014): I solved the problem with code like this:

typedef shared_ptr<ClassA> ClassAPtr;
typedef shared_ptr<ClassB> ClassBPtr;
typedef shared_ptr<ClassC> ClassCPtr;

//...

ClassAPtr a(new ClassA());
ClassBPtr b( new ClassB() );
b->attachA(a);
a->attachB(b);
ClassCPtr c( new ClassC() );
c->attachB(b);
c->attachA(a);

c->run();
b->run();
a->run();

While in each of those classes I have atomic variable _running:

atomic<bool> _running;

Also I have the following synchronization code in each thread loop:

while (
        _this->_a == NULL || !_this->_a->isRunning()
        ||
        _this->_b == NULL || !_this->_b->isRunning()
        )
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
Was it helpful?

Solution

You're passing a copy of shared_ptr<A> to the instance of B. When you assign a new object to a by calling reset, the copy stored in b has no idea this has happened. This is why it never sees the update.

Calling shared_ptr::reset makes that instance of the shared_ptr relinquish ownership of the managed object, which means decrementing the use_count and destroying the object if that instance happens to be the sole owner. If it's not the sole owner, then the other shared_ptrs managing that object become responsible for managing its lifetime. In either case, the instance that you invoked reset is now free to take ownership of another object that you may have passed as an argument. In your case this is all a bit simpler since there is no object being managed initially, but the same logic applies. The copy stored within b is in no way linked to a after the call to reset.

Say your class A implements two member functions, activate() and is_active(), with obvious functionality. Also assume that constructing an instance of A leaves it in a deactivated state until you call activate(). Then you could solve this problem as follows:

auto a = make_shared<A>();
B b(a);
a->activate();

// within the thread
while (!_this->_a->is_active()) {}

Even so, you'd need to use some synchronization primitive to a prevent data race. For instance, if activate() sets a boolean data member, the type of that member should be std::atomic<bool> or std::atomic_flag, instead of a plain bool. The same applies to any other data members that maybe read from and written to from different threads.

OTHER TIPS

A std::shared_ptr is not thread-safe. You need to use different synchonization mechanisms. std::future is an object that can be used to wait for another thread. You can create one using an std::packaged_task.

What might be happening is that a local copy is made of the internals of your shared pointer. This cached copy isn't aware of updates in your main memory. The pointer is updated, but this is dependent on which compiler, what optimizations are active and CPU architecture.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top