Home > Back-end >  My code is exiting with use-after-free error but I don't know why?
My code is exiting with use-after-free error but I don't know why?

Time:12-22

I'm a multithreading beginner and I'm wring the below code:

#include <iostream>
#include <functional>
#include <thread>
#include <mutex>
#include <future>
#include <unistd.h>

using namespace std;

class Foo {
 public:
  Foo() {}
  ~Foo() { cout << "foo dtor" << endl; }

  void first(function<void()> printFirst) {
    printFirst();
  }

  void second(function<void()> printSecond) {
    printSecond();
  }

  void third(function<void()> printThird) {
    printThird();
  }
};

int main() {
  Foo foo;
  thread t1([&foo] {
    function<void()> print11 = [] { cout << "1" << endl; };
    foo.first(print11);
  });
  thread t2([&foo] {
    function<void()> print21 = [] { cout << "2" << endl; };
    foo.second(print21);
  });
  thread t3([&foo] {
    function<void()> print31 = [] { cout << "3" << endl; };
    foo.third(print31);
  });
  sleep(2);
//  t1.join();
//  t2.join();
//  t3.join();
  return 0;
}

And I'm getting the error

"Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

If I uncomment the three join() lines, the program exit normally.

I have a feeling that the error is due to use after free, but cannot explain why. What I'm thinking is the main thread will sleep 2 seconds before it really finishes, and during the main thread is sleeping, the t1, t2, t3 should already finish. Hence, even if foo is destroyed, the three thread won't use it after 2 seconds. As far as I understand, it should not have use after free problem. Could anybody explain? Thanks!

CodePudding user response:

This does not look to me like a use after free problem, and it's not a race condition either. It's required behavior. Attempting to destroy a thread object that is in a joinable state (which yours would be under the circumstances) is required to terminate the program (N4835, §[thread.thread.destr]/1):

~thread();

  1. If joinable(), calls terminate(). Otherwise, has no effects. [Note: Either implicitly detaching or joining a joinable() thread in its destructor could result in difficult to debug correctness (for detach) or performance (for join) bugs encountered only when an exception is thrown. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable. —end note]

A thread is joinable() from the time it starts to run until the time it is join()ed or detach()ed.

Summary

Destroying a thread must abort the program if the thread is in a joinable state. To avoid this, join() the thread(s) before destroying them.

CodePudding user response:

The operating system is under no obligation to run your other threads when you sleep(2). It could let Google Chrome have that time slice, or it could use it to do its own background tasks, or it could just sit there on its thumbs.

That logic you just went through of "This thread should only last two seconds at most, so sleeping will do it" is called a race condition. You've got two threads and you're making big assumptions about what order things in those threads will happen, without actually enforcing those assumptions. Effectively, your threads enter into a race: if the three child threads win the race, then your program works fine, but if the main thread wins the race, then your program exhibits undefined behavior. And if your program has a chance of exhibiting undefined behavior, then that means that your program's behavior is undefined.

By adding the join calls, you enforce your assumption. You demand that the main thread cannot exit until the other three threads finish, which is what you were implicitly assuming before. This makes your program's behavior defined.

  • Related