1

I have this weird problem where thread I created does not terminate even after it exits from the thread function. I create the thread so:

typedef void(*Task)(void*);        
AsyncWorker(Task proc, void* arg): thd_(NULL)  {
   thd_ = new std::thread(proc, arg);
}

~AsyncWorker() {
   if (thd_) {
     if(thd_->joinable())
        thd_->join(); // does not return from here
     delete thd_;
    }
 }

This is the task that the thread executes:

 static void RunLoop(void* arg)
 {
    if (!arg)
        return;

    SomeObject* thiz = static_cast<SomeObject*>(arg);
    while( !(thiz->done_) ) {
       thiz->DoInLoop();
    }
    return;
 }

I set the member SomeObject::done_ to true from the main thread and delete AsyncWorker. When I step through the debugger I can see that the thread has exited from the RunLoop function but call to join in the dtor hangs. The call stack for both the thread and the main thread shows

[External Code]
[No symbols loaded for ntdll.dll]

What could be the problem? The SomeObject::DoInLoop method does wait on a mutex but I signal the mutex before deleting AsyncWorker object so that the thread can go past that and in any case if the thread has exited from the thread proc it is clearly not holding on to any mutexes, right? What is frustrating is that the call stack does not tell me where it is stuck.

Initially, I thought it was a problem how I was using std::thread (I am using them for the first time) but the I tried the same with Windows threads and got the same problem. So I must be doing something wrong.

Edit: I initially tagged the problem as vs2012 but I am actually using vs2013 sp1.

341008
  • 9,862
  • 11
  • 52
  • 84
  • Are you sure, your debugger works correctly? Have you e.g. tried to put a printf at the end of the RunLoop function? Btw.: is the task creation via new really necessary? – MikeMB May 15 '14 at 13:49
  • 1
    possible duplicate of [std::thread::join() hangs if called after main() exits when using VS2012 RC](http://stackoverflow.com/questions/10915233/stdthreadjoin-hangs-if-called-after-main-exits-when-using-vs2012-rc) – MSalters May 15 '14 at 14:26
  • 1
    Threads appear to be too hard for Microsoft; they haven't been able to fix this despite this being noticed quite a while ago. – MSalters May 15 '14 at 14:29
  • @MSalters I had seen that question before posting but as that hang happens after main() exits, it seemed my problem could be different. Also, I cannot see any locks that my thread could be holding on to. – 341008 May 15 '14 at 14:34
  • @MikeMB No `new` isn't really necessary, but removing it does not solve the problem. – 341008 May 15 '14 at 14:37
  • @MSalters If this is a known bug, are there any known workarounds? – 341008 May 15 '14 at 14:40
  • `WaitForSingleObject(msgThread.native_handle()` instead of join. – MSalters May 15 '14 at 14:42
  • @MSalters I tried that but got the same result. Also, as mentioned in the questions, I replaced `std::thread` with native win32 thread (via `CreateThread`) but saw no difference. Which is why I think it might be my error rather than Microsoft's. – 341008 May 15 '14 at 14:46
  • How is `SomeObject::done_` protected from simultaneous access by the main thread and the async thread? If you gave us an [SSCCE](http://www.sscce.org/) this would be easier to diagnose. – Casey May 15 '14 at 14:55
  • Well, I'm not an expert on C++ programming, but I've always been told that joining a thread in a destructor is a very, very bad idea. What if you try calling it from some other method manually, rather than doing it in a destructor? Does the problem persist? – Luaan May 15 '14 at 14:58
  • @Casey It isn't. async thread only reads it and I don't care if it iterates a few more times before reading the updated value. – 341008 May 15 '14 at 14:58
  • [This program](http://ideone.com/gULkDk) works for me (not in ideone, but in my copy of VS2012). The problem must be somewhere in the code you haven't shown. – Igor Tandetnik May 15 '14 at 15:00
  • 3
    @341008 That's your problem then - programs with data races have undefined behavior. Make `done_` a `std::atomic`. (In other words: the compiler can see that `DoInLoop` doesn't change the value of `done_`, so the condition test is only performed once before the `while` loop which then runs forever.) – Casey May 15 '14 at 15:05
  • @Casey No, that's not it. As I said, `RunLoop` breaks out of the loop and exits. – 341008 May 15 '14 at 15:30
  • @Casey That would be my first suggestion too, but the OP also said that when he follows the code in a debugger, the thread does exit, and the join call still hangs. Now, debuggers do change behaviour of programs, but a simple `printf` would help a lot in debugging. – Luaan May 15 '14 at 15:31
  • 2
    Well, I think it's time you made a simple, complete code that replicates your issue. Either you'll realize your mistake in the process, or you'll give us something to work with, given that the general solutions got us nowhere so far :) And as Casey said, you really don't want to use `done_` like this - it's actually possible for the thread to be stuck forever in the cycle, even though you've set `done_` to `true` in a different thread. One of the hard things about multi-threading is that many constraints no longer apply - `done_` might be stored in a register for all you know. – Luaan May 15 '14 at 15:57

0 Answers0