Search code examples
mingwc++11atomicboost-thread

mingw 4.6.1 atomic fail?


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    

Solution

  • 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.