Consider this code (extracted from Simple-Web-Server, but knowledge of the library shouldn't be necessary to answer this question):
HttpServer server;
thread server_thread;
server.config.port = 8080;
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
string content = "Hello world!"
*response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.size() << "\r\n\r\n" << content;
};
server_thread = thread([&server]() {
server.start();
});
HttpServer::default_resource
is a std::unordered_map, which, to my understanding, isn't thread-safe. port
is an unsigned short.
Assuming my understanding of C++ memory fences is correct, server
, as seen by the new thread, might not be in a valid state as the main thread might not have written the changes to port
and default_resource
to memory accessible from other threads. As such, server.start()
might not work properly.
To fix this, I would have to change the code by adding to atomic_thread_fence
s:
HttpServer server;
thread server_thread;
server.config.port = 8080;
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
string content = "Hello world!"
*response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.size() << "\r\n\r\n" << content;
};
atomic_thread_fence(memory_order_release);
server_thread = thread([&server]() {
atomic_thread_fence(memory_order_acquire);
server.start();
});
Is my understanding correct, and are both the atomic_thread_fence
s necessary?
In other words: when the thread function gets invoked, it is synchronized with everything that happened in the parent thread up until
std::thread
gets constructed, in the parent thread.No explicit memory barriers/fences, of this kind, are needed.