'How to address the "Lock was destructed while held" Visual C++ runtime assertion

In a program that uses DirectX to render a 3D component I use a dedicated rendering thread that apparently gets created if I call DispatcherQueueController::CreateOnDedicatedThread(). What needs to be rendered is influenced by actions done on the main thread which is why I use a scoped_lock to synchronize the access.

The code that creates the rendering thread and the code that runs within it looks as follows:

void MyCustomView::StartRenderingLoop()
{
  if(!_isRenderLoopRunning)
  {
    _renderLoopController = DispatcherQueueController::CreateOnDedicatedThread();
    _isRenderLoopRunning  = _renderLoopController.DispatcherQueue().TryEnqueue([this]()
    {
      while(_isRenderLoopRunning)
      {
        Concurrency::critical_section::scoped_lock lock(_criticalSection);

        if(_renderer->Render())
        {
          Present();
        }

        // Halt the thread until the next vblank is reached.
        // This ensures the app isn't updating and rendering faster than the display can refresh,
        // which would unnecessarily spend extra CPU and GPU resources. This helps improve battery life.
        _dxgiOutput->WaitForVBlank();
      }
    });
  }
}

The associated member variables in the C++ header file looks as follows:

private:
  concurrency::critical_section _criticalSection;
  winrt::Microsoft::UI::Dispatching::DispatcherQueueController _renderLoopController{ nullptr };
  bool _isRenderLoopRunning  = false;

In order to stop the rendering thread the destructor of the 3D component contains the following code:

MyCustomView::~MyCustomView()
{
  _isRenderLoopRunning = false;
  _renderLoopController.ShutdownQueueAsync();
}

When the 3D component gets destroyed, the Visual C++ runtime throws an assertion that looks as follows:

Debug Assertion Failed!

Program: MyAppPackage\CONCRT140D.dll
File: d:\agent\_work\18\s\src\vctools\crt\crtw32\concrt\rtlocks.cpp
Line: 1001

Expression: Lock was destructed while held

The callstack that I can obtain looks as follows:

concrt140d.dll!Concurrency::critical_section::~critical_section() + 79 bytes    Unknown
MyCustomComponents.dll!winrt::MyCustomComponents::implementation::BaseView::~BaseView() C++
MyCustomComponents.dll!winrt::implements<winrt::MyCustomComponents::implementation::MyCustomView,winrt::MyCustomComponents::MyCustomView,winrt::MyCustomComponents::implementation::BaseView,winrt::no_module_lock>::~implements<winrt::MyCustomComponents::implementation::MyCustomView,winrt::MyCustomComponents::MyCustomView,winrt::MyCustomComponents::implementation::BaseView,winrt::no_module_lock>()   C++
MyCustomComponents.dll!winrt::MyCustomComponents::implementation::MyCustomView_base<winrt::MyCustomComponents::implementation::MyCustomView,winrt::MyCustomComponents::implementation::BaseView>::~MyCustomView_base<winrt::MyCustomComponents::implementation::MyCustomView,winrt::MyCustomComponents::implementation::BaseView>() C++
MyCustomComponents.dll!winrt::MyCustomComponents::implementation::MyCustomView::~MyCustomView() Line 32 C++

I'm struggling to understand whether the assertion Lock was destructed while held points to an actual problem or whether it is safe to ignore this. If that is an actual problem, how would I address it properly?

Edit:

The call stack points to BaseView from which MyCustomView inherits. The simplified BaseView looks as follows:

#pragma once

#include <concrt.h>


namespace winrt::MyNamespace
{
  struct BaseView : BaseViewT<BaseView>
  {
  protected:
    concurrency::critical_section _criticalSection;

    // Several methods
  };
}

There is no constructor or destructor implementation of that class. But it constructs the _criticalSection as shown above.



Solution 1:[1]

To me it looks like the problem is that your mainthread is destructing your objects, which contains the other thread.

You are trying to solve this by setting _isRenderLoopRunning to false, but other tread will will not be synchronized with that new value. So the other thread continues running, while you destruct the object with the main thread, which gives you this error.

You could make some methods that locks the access to this variable, so it has a lock when it is read, and also for when it is changed.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Cristik