shared_ptr
itself is thread safe by design. So the code is safe with respect to returning the shared_ptr
instance.
More precisely: it is required to copy config_
to be thread safe. And even the lock is not dispensable.
Assuming that your lock prevents config_
from being modified while the function executes, the copy of config_
is done while the lock is held. This is essential because shared_ptr
does not provide strong thread safety. The latter is required to safely read a shared pointer instance that could be modified asynchronously.
The problem behind the scenes is that after shared_ptr
read the pointer of config_
and before it incremented the reference counter config_
might just being assigned with a new object. In this case the assignment operator decrements the reference counter, and if it returns to 0 (because no reference except for config_
points to the old object) it discards the old object. Unfortunately your thread has read the old object pointer and tries to increment the counter of the object that is currently destroyed. This is UB.
The lock together with the copy prevents from this scenario, because at the time the lock is released the reference counter of the object behind config_
is at least 2. One for your copy and one for config_
itself. So the object cannot be destroyed by an assignment to config_
.