-1

I got this exception in my program: 0x00007FFD187CF61E (ucrtbase.dll) My project is C++/MFC and source is below.

I confirm this code works in my other project which is a console project.

ReloadWatchdog.h

#pragma once
class ReloadWatchdog
{
public:
    static void Reload();
};

ReloadWatchdog.cpp

#include "pch.h"
#include "ReloadWatchdog.h"
#include <thread>
void ReloadWatchdog::Reload()
{
    while (true)
    {
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(std::chrono::minutes(4));
        try {
            call_method();
        }
        catch (...) {
        }
    }
}

main

BOOL CWatchDogPI336Dlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // ↓this occured exception
    std::thread th1(ReloadWatchdog::Reload);

    return TRUE;
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Teppei Abe
  • 71
  • 1
  • 8
  • You don't join a joinable thread at the function exit. A console application doesn't have OnInitDialog, so you confirm something else but not the shown code. – 273K Apr 20 '23 at 04:55
  • You have race conditions : CWatchDogPI336Dlg can be destroyed while the thread is still runnig. Also you never should use sleep (specially not for such long durations) but use [std::condition_variable](https://en.cppreference.com/w/cpp/thread/condition_variable)::wait_for to be able to stop the thread before 4 minutes have passed. And then wait in destructor for thread to have finished doing its thing – Pepijn Kramer Apr 20 '23 at 05:33
  • 1. Thread is not joined. 2. Thread dont have while exit option. – Nagappa Apr 20 '23 at 05:44
  • @PepijnKramer There is no race here. The issue is strictly single-threaded, and can be reproduced using this code: `struct X { ~X() { std::terminate(); } }; int main() { X x(); }`. – IInspectable Apr 20 '23 at 07:52
  • The race would be in CWatchDogPI336Dlg::~CWatchDogPI336Dlg() which will happily destruct itself even while the thread is still running. When that thread wakes up again and calls call_method() it does so on a destroyed object. – Pepijn Kramer Apr 20 '23 at 08:02
  • 1
    @PepijnKramer No, it won't. The operating system thread has no reference to the C++ `std::thread` object. And even if it did, there wouldn't be any time to do anything: `std::thread::~thread()` has already called `std::terminate()`. – IInspectable Apr 20 '23 at 08:09
  • `It blocks the current thread until the execution of the thread is completed on which join() is called. If you do not specify join() or dettach() on the thread then it will result in runtime error as the main/current thread will complete its execution and the other thread created will be still running.`[What does std::thread.join() do](https://stackoverflow.com/questions/15148057/what-does-stdthread-join-do) – Minxin Yu - MSFT Apr 24 '23 at 01:43

1 Answers1

3

C++11's std::thread is designed to own the underlying operating system thread. By default, a std::thread object needs to outlive the operating system thread.

The design is understandable, but has an unfortunate consequence: Failure to uphold the invariant between the two lifetimes can only be observed once std::thread's destructor has started executing. Too late to throw an exception, so the implementation calls std::terminate() instead.

This is what's happening here:

BOOL CWatchDogPI336Dlg::OnInitDialog()
{
    // ...
    std::thread th1(ReloadWatchdog::Reload);

    return TRUE;
} // th1 is destroyed here

Depending on the specific needs, there are three options to address the issue:

  1. Calling detach() on th1, which relaxes the lifetime requirements. The std::thread instance and the operating system thread can live for however long either one needs to.
  2. Making th1 a non-static class member of CWatchDogPI336Dlg. This doesn't alleviate the core issue rather than postponing it. When ~CWatchDogPI336Dlg is done, the operating system thread must have run to completion.
  3. Using C++20's std::jthread instead. The interesting difference is that its destructor will wait for the operating system thread to terminate. This is a blocking call, so it's not immediately applicable here.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • Thank you for your comment. I understood what you wrote here.I'm trying to using std::jthread. – Teppei Abe Apr 20 '23 at 12:21
  • 1
    @TeppeiAbe Replacing `std::thread` with `std::jthread` turns your application from one that terminates to one that stops responding. Neither one is desirable. – IInspectable Apr 20 '23 at 12:29