5

Is static local variables safe for a class that creates/uses std::threads?

Because when I use something like this:

logger& logger::get_instance(void)
{
    static logger lg;
    return lg;
}

And try to exit (force close) the executable, it crashes/exits improperly (somethings the Visual Studio 2012 debugger even crashes).

When I don't do that, the program exits gracefully when i force close.

Here's the stack call when it crashes

        ntdll.dll!77c10dbd()    Unknown
        [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
        ntdll.dll!77b7bfdc()    Unknown
        kernel32.dll!75b55bab() Unknown
    >   msvcr110d.dll!__crtCreateThreadpoolWait(void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * pfnwa, void * pv, _TP_CALLBACK_ENVIRON_V1 * pcbe) Line 569  C
        msvcr110d.dll!Concurrency::details::RegisterAsyncWaitAndLoadLibrary(void * waitingEvent, void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * callback, void * data) Line 675    C++
        msvcr110d.dll!Concurrency::details::ExternalContextBase::PrepareForUse(bool explicitAttach) Line 120    C++
        msvcr110d.dll!Concurrency::details::ExternalContextBase::ExternalContextBase(Concurrency::details::SchedulerBase * pScheduler, bool explicitAttach) Line 52 C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::GetExternalContext(bool explicitAttach) Line 1579    C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::AttachExternalContext(bool explicitAttach) Line 1527 C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 569 C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 402    C++
        msvcr110d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 616 C++
        msvcr110d.dll!Concurrency::critical_section::lock() Line 1017   C++
        msvcp110d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 65    C++
        msvcp110d.dll!_Mtx_lock(_Mtx_internal_imp_t * * mtx) Line 144   C++
        escobar.exe!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68   C++
        escobar.exe!std::_Mutex_base::lock() Line 43    C++
        escobar.exe!std::unique_lock<std::mutex>::unique_lock<std::mutex>(std::mutex & _Mtx) Line 228   C++
        escobar.exe!escobar::utilities::blocking_queue<escobar::logging::log_message *>::interrupt() Line 71    C++
        escobar.exe!escobar::logging::log_worker::~log_worker() Line 17 C++
        escobar.exe!escobar::logging::log_worker::`scalar deleting destructor'(unsigned int)    C++
        escobar.exe!escobar::logging::logger::close() Line 72   C++
        escobar.exe!escobar::logging::logger::~logger() Line 27 C++
        escobar.exe!`escobar::logging::logger::get_instance'::`2'::`dynamic atexit destructor for 'lg''()   C++
        msvcr110d.dll!doexit(int code, int quick, int retcaller) Line 585   C
        msvcr110d.dll!_cexit() Line 410 C
        msvcr110d.dll!__CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 296  C
        msvcr110d.dll!_CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 210   C
        ntdll.dll!77bb2846()    Unknown
        ntdll.dll!77bb2893()    Unknown
        ntdll.dll!77bc09c8()    Unknown
        ntdll.dll!77bc08ad()    Unknown
        KernelBase.dll!75525bbb()   Unknown
        KernelBase.dll!75525c51()   Unknown
        kernel32.dll!75b58543() Unknown
        ntdll.dll!77bbac69()    Unknown
        ntdll.dll!77bbac3c()    Unknown

Here are a couple of the functoin

log_worker::~log_worker(void)
{
    this->queue.interrupt();
    service.join();
}

void log_worker::run(void)
{
    while (true)
    {
        log_message* msg;
        if (this->queue.dequeue(msg) == false)
            break;

        this->lg->print_log_message(msg);

        delete msg;
    }
}


    bool dequeue(T& item)
    {
        std::unique_lock<std::mutex> lock(m);

        // handles spurious wakeups
        while (!this->data_available && !this->interrupted)
            cv.wait(lock);

        if (this->interrupted)
            return false;

        item = std::move(this->front());
        this->pop();
        if (this->empty())
            this->data_available = false;
        return true;
    }

    void interrupt(void)
    {
        std::unique_lock<std::mutex> lock(m);
        this->interrupted = true;
        cv.notify_all();
        printf("notified threads...\n");
    }
TheAJ
  • 10,485
  • 11
  • 38
  • 57
  • What guarantees does the C++ standard make, if any, about the order in which objects with static storage duration are destroyed? – ta.speot.is Feb 03 '13 at 06:16
  • Also you might get more information by enabling the Microsoft Symbol Server in Visual Studio. http://through-the-interface.typepad.com/.a/6a00d83452464869e201538f8a21bc970b-pi – ta.speot.is Feb 03 '13 at 06:30
  • @TheAJ is your `log_worker` static as well? – billz Feb 03 '13 at 06:31
  • @ta.speot.is: They're destroyed in the reverse order they were constructed. – Chris Dodd Feb 03 '13 at 06:34
  • @TheAJ is `SchedulerBase` your own class? – billz Feb 03 '13 at 06:41
  • No, SchedulerBase is not my class. And log_worker is dynamically allocated in the logger class. ~logger() calls ~log_worker() (the stack call is in descending order) – TheAJ Feb 03 '13 at 06:43
  • @billz You can tell `SchedulerBase` is not his class, it's in `msvcr110d.dll` - MicroSoft Visual C++ Runtime 11.0 Debug – ta.speot.is Feb 03 '13 at 06:44
  • @TheAJ you need to delete `log_worker` inside logger destructor. your ` log_worker is dynamically allocated in the logger class` means you new `log_worker`, right? – billz Feb 03 '13 at 06:48
  • 1
    @TheAJ - stop deleting stuff on shutdown. – Martin James Feb 03 '13 at 10:47

2 Answers2

5

Looks like you have a detached thread SchedulerBase(could be any scheduled thread which still uses logger) which is still running while your application is stopped and logger is destroyed which caused the crash.

And log_worker is dynamically allocated in the logger class.

  • You need to make sure all threads who use logger instance are shutdown properly before logger is destroyed
  • delete log_worker in logger destructor
billz
  • 44,644
  • 9
  • 83
  • 100
  • The easiest way of ensuring that 'all threads who use logger instance are shutdown properly before logger is destroyed' is to not explicitly destroy the logger. – Martin James Feb 03 '13 at 10:49
1

I simply had to stop deleting things on shutdown. When you close the console using the 'X' button, it's not a proper shutdown, so It's pointless trying to shutdown threads.

TheAJ
  • 10,485
  • 11
  • 38
  • 57