So I discovered that gcc already had a working implementation of std::atomic
so I wanted to try it out, here is my example using boost thread 1.48 and std::atomic
:
#include <iostream>
#define BOOST_THREAD_USE_LIB
#include <boost/thread.hpp>
#include <atomic>
#include <string>
using namespace std;
using namespace boost;
class Server
{
public:
atomic<bool> running;
thread main_server_thread;
void func1()
{
while(running)
{
//do stuff
}
}
void func2()
{
while(running)
{
//do stuff
}
}
void func3()
{
while(running)
{
//do stuff
}
}
void func4()
{
string input;
while(running)
{
//do stuff
input.clear();
getline(cin,input);
if(input=="quit")running=false;
}
}
void main_function()
{
thread thrd1(bind(&Server::func1,this));
thread thrd2(bind(&Server::func2,this));
thread thrd3(bind(&Server::func3,this));
thread thrd4(bind(&Server::func4,this));
while(running)
{
//do stuff
}
thrd1.join();
thrd2.join();
thrd3.join();
thrd4.join();
}
Server()
{
running=true;
main_server_thread = thread(&Server::main_function,this);
}
};
int main()
{
Server* serv = new Server();
serv->main_server_thread.join();
return 0;
}
Now while running
remains true
all is fine but when the user inputs quit
and running
is set to false
some threads end and some don't. This happens with and without optimizations. Is this working as intended? In my understanding of atomics the reads shouldn't conflict with the one write so the threads should see the running=false
at some point.
EDIT: Disassembly of func1:
void func1()
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 18 sub $0x18,%esp
{
while(running)
6: 90 nop
7: 8b 45 08 mov 0x8(%ebp),%eax
a: 89 04 24 mov %eax,(%esp)
d: e8 00 00 00 00 call 12 <__ZN6Server5func1Ev+0x12>
12: 84 c0 test %al,%al
14: 75 f1 jne 7 <__ZN6Server5func1Ev+0x7>
{
//do stuff
}
}
16: c9 leave
17: c3 ret
As the disassembly shows, this program, when compiled with mingw, does not treat running
as an atomic: it is loaded with a regular mov 0x8(%ebp),%eax
without synchronization.
Compare to the output of gcc-4.6.2 on linux/x86_64:
_ZN6Server5func1Ev: // Server::func1()
jmp .L78
.L79:
// do stuff
.L78:
mfence
movzbl (%rdi), %eax // load from `running`
mfence
testb %al, %al
jne .L79
ret
Either mingw doesn't support that yet, or something is missing in its configuration.