The client holds a shared pointer to a value say double, and a server updates this value on another detached thread by holding a weak_pointer, the server checks if the weak_pointer is expired and if it is not, it is deleted from a safe vector. I suspect this would not work as the shared_ptr could be destructed from the client side between my read of expired() (which I think is thread safe as I imagine it refers to the atomic counter of shared_ptr) and my update of the value. Is there a way to have the check expired() and a lambda function updating the value before it is destructed please? I mean:
struct server
{
public:
static void subscribe(const std::shared_ptr<double>& d)
{
m_values.safe_push_back(d); //safely pushes back in the vector
}
void update()
{
auto updater = [](std::weak_ptr<double>& wd)
{
if(wd.expired()) wd = nullptr;
else *wd += 2.0; //This is not thread safe I guess?
};
m_values.safe_remove_all(nullptr);
m_values.safe_visit(updater);
};
private:
static safe_vector<std::weak_ptr<double>> m_values;
}
struct client
{
void subcribe_sleep_print(const double random_seconds)
{
std::shared_ptr<double> d = std::make_shared<double>(0.0); //for the sake of the example assume we create subscribe before sleep
server::subscribe(d);
sleep_for_seconds(random_seconds); //sleeps for some random time in seconds.
std::cout << *d << std::endl;
}
}
Imagine server::update and client::subcribe_sleep_print are running on different threads. This is not safe as the shared_ptr might get destructed while the server is writing? Is there a way to this with no user (me) defined atomics or mutexes? I don't mind if it's used in the background as long as I don't add them (atomic/mutex) myself as I know the shared pointer uses an atomic counter already.
EDIT: I am not using double in my program, I am using a thread safe object :) So the operation += can be assumed thread safe. Sorry.
No, it's not thread-safe. You are facing a race-condition where the pointer may expire after all just after you have checked for expiration. In that case, you have now dereferenced a
nullptr.So the way to go is to explictly use
std::weak_ptr<T>::lock()to try and obtain the associated shared pointer (which is also whatexpired()did internally), and only if that obtained a valid pointer you may do a read- or write-access.If you didn't obtain a valid shared pointer, treat it just as if
expired()had returned false.I'm certain you've got the order wrong. You had intended to first update, and then weed out the
nullptryour update callback had produced.