How to avoid ahead of desturction in derived class?

Viewed 77

I launched a thread in based thread. There is a pure virtual function called in this thread. I implement it in the derived class.

#include <iostream>
#include <memory>
#include <thread>
struct Base {
  std::unique_ptr<std::thread> th;
  virtual void foo() = 0;
  void bar() { 
    th = std::make_unique<std::thread>(
      [this](){
        foo();
      }
    );
  }
  virtual ~Base(){
    th->join();
  }
};

struct Derived : public Base {
  virtual void foo() override {
    std::cout << "impl in derived" << std::endl;
  }
};
int main() {
  Derived d;
  d.bar();
  return 0;
}

However, in these codes, the derived object will decontruct before the base object.

pure virtual method called
terminate called without an active exception
Aborted (core dumped)

How to control the destruction order in this situation?

1 Answers

This is a concurrency problem, and what happens is undefined behavior. It is entirely possible that if you build a different configuration (for example, switch from Debug to Release build) the application will break differently.

Concretely, I believe that when main finishes execution and goes out of scope, your thread is just started. This calls all destructions, which ends up (in your implementation) with foo pointing to the base class implementation (at the end of the derived destructor) followed by main being blocked in the base destructor - in the call to join.

How to fix:

move the join in a separate function and call it explicitly:

struct Base {
  std::unique_ptr<std::thread> th;
  virtual void foo() = 0;
  void bar() { 
    th = std::make_unique<std::thread>(
      [this](){
        foo();
      }
    );
  }
  virtual ~Base() = default;

  void join() { th->join(); }
};

int main() {
  Derived d;
  d.bar();
  d.join(); // prevent the destruction of the derived part
            // and main blocking before completing the base
            // destructor execution.
  return 0;
}
Related