mercredi 30 janvier 2019

Worker threads, running in a DLL gets killed, on application shutdown, before one can gracefully shutdown them

I, for some reason, need to have a global object in a .dll, which manages a std::thread. It is implemented in a following way:

#include "Header.h"
#include <thread>
#include <condition_variable>
#include <mutex>

class Foo
    {
public:
    Foo () : m_closeThread (false)
        {
        m_thread = std::thread (&Foo::ThreadProc, this);
        }

    ~Foo ()
        {
            {
            std::unique_lock<std::mutex> lock (m_mutex);
            m_closeThread = true;
            }

        m_cv.notify_one ();
        m_thread.join ();
        }

private:
    void ThreadProc ()
        {
        while (true)
            {
            std::unique_lock<std::mutex> lock (m_mutex);
            m_cv.wait (lock, [this] () { return m_closeThread; });
            if (m_closeThread)
                break;
            }
        }

private:
    bool m_closeThread;
    std::thread m_thread;
    std::condition_variable m_cv;
    std::mutex m_mutex;
    };

Foo& GetFoo ()
    {
    static Foo foo;
    return foo;
    }

extern "C" void Function ()
    {
    auto& foo = GetFoo ();
    }

However, when, the application is closed, before the ~Foo is executed, all worker threads, of the .dll, get killed, or as the output window of MSVS2015 says:

The thread 0x1040 has exited with code 0 (0x0).

And, due to this fact (Source0, Source1), the application blocks on m_cv.notify_one (); call, if one uses Windows 7 (doesn't block on Windows 8 and above).

The fact, that it blocks on one particular version of Windows, while not on others, makes me think, that some sort of UB is to blame (such as DLL unload ordering related issue, since such issue is not reproducible if such object is not in a .dll), but I fail to think of solution, that allows me to gracefully shutdown the thread, while having the object global (since, one would need to do major application restructuring, to make it not global).

So, can one shutdown the thread gracefully, before it is killed by the Windows platform?

Side note 0:

For the sake of example completeness,

this is the DLLMain:

#include <Windows.h>

BOOL APIENTRY DllMain (HMODULE, DWORD, LPVOID) { return TRUE; }

This is the Header of the .dll:

#pragma once
extern "C" void Function ();

This is the Console application, that uses said .dll:

#include "..\Test\Header.h"
#include <chrono>
#include <thread>

int main ()
    {
    Function ();
    using namespace std::chrono_literals;
    std::this_thread::sleep_for (2s);
    }

Side note 1:

I am, currently limited to using, at most, C++11 (or, whatever functionality is present in MSVS 2015).

Aucun commentaire:

Enregistrer un commentaire