8

I have the a simple program.

int main() 
{
    std::atomic<bool> b = true;
    ConcurrentQueue<std::string> queue;

    std::thread thread( [&]{
        while ( b ) {
            auto str = queue.wait_and_pop();
            std::cout << *str;
        }
    });

    b = false;
    queue.push( "end" );
    thread.join();
}

ConcurrentQueue<T> is my own implementation of thread safe queue, wait_and_pop is a blocking operation that use std::condition_variable.

this program successfully prints "end" and exits, no problem here. ( there is a bug that b is false when thread started which cause it to exit immediately but that is not relevant here )

But if I wrap all these in a class

class object {
public:
    object() {
        b = true;

        thread = std::thread( [this]{
            while ( b ) {
                auto str = queue.wait_and_pop();
                std::cout << *str;
            }
        });
    }

    ~object() {
        b = false;
        queue.push( "end" );
        thread.join();
    }

private:
    std::atomic<bool> b;
    std::thread thread;
    ConcurrentQueue<std::string> queue;
};

and have a function static variable like

object & func() {
  static object o;
  return o;
}

and main

int main() {
    object & o = func();
}

now the program prints "end" then stuck at destructor of o at line thread.join().

I have tested this with clang and no problem. This seem to only happen in VC11. Why is that?

yngccc
  • 5,594
  • 2
  • 23
  • 33
  • 1
    I don't see anything wrong with the code as-is. One possibility is that you have found a bug in VC11's handling of thread spawning in static initializers, which seems like an edge case that might be buggy. Another is that you have a bug in ConcurrentQueue, which you don't show. – Andy Ross Jun 21 '13 at 16:18
  • When will the static object o get deleted? – doctorlove Jun 21 '13 at 16:20
  • dauphic: it looks correct to me: set the atomic flag, push a record to ensure at least one wakeup after the flag is set, and then wait for the inevitable thread exit. – Andy Ross Jun 21 '13 at 16:20
  • Yes. I misread the code and thought the join was occurring in `main` for some reason. It looks fine. – Collin Dauphinee Jun 21 '13 at 16:21
  • @AndyRoss I replaced my `ConcurrentQueue` with TBB's `tbb::concurrent_bounded_queue` and the problem still persist. – yngccc Jun 21 '13 at 16:32
  • @doctorlove - the static object will never be deleted. It will be destroyed after `main` returns. – Pete Becker Jun 21 '13 at 16:37
  • About your bug with `b` being false when the thread starts, this is because the value initialization of an atomic object is not an atomic operation and doesn't guarantee any memory order / fences. See [this](http://en.cppreference.com/w/cpp/atomic/memory_order) and [this](http://en.cppreference.com/w/cpp/atomic/atomic/atomic). You can use either [ATOMIC_VAR_INIT](http://en.cppreference.com/w/cpp/atomic/ATOMIC_VAR_INIT), or create the atomic and then assign a value to it. – Mikael Persson Jun 21 '13 at 16:51

1 Answers1

5

There recently was a thread with the same problem, but I can't find it anymore.

Basically, there's a deadlock in VS's runtime library when you have a static lifetime object that tries to end a thread in its destructor.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • join before the destructor works... seems like a temporary work around until they fix it. Thanks – yngccc Jun 21 '13 at 16:52