struct X
{
std::mutex m;
std::string str;
void set(std::string s)
{
auto _ = std::unique_lock(m);
str = std::move(s);
}
~X()
{
// auto _ = std::unique_lock(m);
}
}
Is there any part of the standard that guarantees that ~X
will never experience race conditions inside ~string
without the commented line?
The exclusive access to the object and/or lifetime could be managed by an atomic variable with RELAXED semantics (== no other data except the fact of the end of lifetime is synched). We have a mutex to protect the access to the object, so using relaxed operation seems to be ok to synchronize exclusive access/lifetime and mutex to access the data.
If the standard would require ~mutex
to synchronize with the latest unlock
and if we moved the declaration of the mutex below the protected data then we could afford default ~X
but without this requirement, we need always have explicit destructor which locks all the mutexes used to protect any member.
PS to help understand my question use this example https://en.cppreference.com/w/cpp/thread/mutex there is a comment that accessing g_pages
without a lock is safe. Why, which part of the standard guarantees this? The fact that both threads are joined only guarantees no multi-threaded access to the map, but it doesn't guarantee synchronize-with relationship with the latest mutex::unlock
operation. I run many different programs trying to expose race conditions and I am pretty sure that mutex
uses std::atomic_thread_fence
for both lock
and unlock
which synchronizes with thread.join
that must use at least some atomic to work. But the problem is standard doesn't require mutex
to use std::atomic_thread_fence
and it it just happens that all known to me implementations use it.
#include <chrono>
#include <iostream>
#include <map>
#include <mutex>
#include <string>
#include <thread>
std::map<std::string, std::string> g_pages;
std::mutex g_pages_mutex;
void save_page(const std::string &url)
{
// simulate a long page fetch
std::this_thread::sleep_for(std::chrono::seconds(2));
std::string result = "fake content";
std::lock_guard<std::mutex> guard(g_pages_mutex);
g_pages[url] = result;
}
int main()
{
std::thread t1(save_page, "http://foo");
std::thread t2(save_page, "http://bar");
t1.join();
t2.join();
// safe to access g_pages without lock now, as the threads are joined
for (const auto &pair : g_pages)
std::cout << pair.first << " => " << pair.second << '\n';
}
Aucun commentaire:
Enregistrer un commentaire