Using Concurrency::critical_section::lock() in the constructor for a global object pre-main

Answered Using Concurrency::critical_section::lock() in the constructor for a global object pre-main

  • vendredi 13 avril 2012 18:25
     
      A du code

    I hit an assert in the function Security::EncodePointer() in utils.cpp, line 692 when I try to lock a C++11 std::recursive_mutex in the constructor for a global object. The constructor executes before main is entered and triggers the assert below:

        PVOID Security::EncodePointer(PVOID ptr)
        {
            CONCRT_COREASSERT(Security::s_initialized != 0);
            return (PVOID)((ULONG_PTR)(ptr) ^ Security::s_cookie);
        }
    

    The call stack is as follows:

     	EAThreadTest.exe!Concurrency::details::_ConcRT_CoreAssert(const char * value=0x0000000140177a08, const char * filename=0x00000001401779b0, int lineno=0x000002b4) Line 370	C++
     	EAThreadTest.exe!Concurrency::details::Security::EncodePointer(void * ptr=0x0000000076843630) Line 693	C++
     	EAThreadTest.exe!Concurrency::details::HardwareAffinity::InitializeSetThreadGroupAffinityFn() Line 238	C++
     	EAThreadTest.exe!Concurrency::details::ResourceManager::InitializeSystemFunctionPointers() Line 714	C++
     	EAThreadTest.exe!Concurrency::details::ResourceManager::RetrieveSystemVersionInformation() Line 764	C++
     	EAThreadTest.exe!Concurrency::details::ResourceManager::InitializeSystemInformation(bool fSaveTopologyInfo=false) Line 942	C++
     	EAThreadTest.exe!Concurrency::details::ResourceManager::GetCoreCount() Line 290	C++
     	EAThreadTest.exe!Concurrency::GetProcessorCount() Line 43	C++
     	EAThreadTest.exe!Concurrency::SchedulerPolicy::_ResolvePolicyValues() Line 261	C++
     	EAThreadTest.exe!Concurrency::SchedulerPolicy::_Initialize(unsigned __int64 _PolicyKeyCount=0x0000000000000000, char * * _PArgs=0x00000000001af640) Line 111	C++
     	EAThreadTest.exe!Concurrency::SchedulerPolicy::SchedulerPolicy(unsigned __int64 _PolicyKeyCount=0x0000000000000000, ...) Line 72	C++
     	EAThreadTest.exe!Concurrency::details::SchedulerBase::GetDefaultScheduler() Line 642	C++
     	EAThreadTest.exe!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 570	C++
     	EAThreadTest.exe!Concurrency::details::SchedulerBase::CurrentContext() Line 399	C++
     	EAThreadTest.exe!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout=0xffffffff) Line 611	C++
     	EAThreadTest.exe!Concurrency::critical_section::lock() Line 952	C++
     	EAThreadTest.exe!mtx_do_lock(_Mtx_internal_imp_t * * mtx=0x00000000002e2560, const xtime * target=0x0000000000000000) Line 84	C++
     	EAThreadTest.exe!_Mtx_lock(_Mtx_internal_imp_t * * mtx=0x00000000002e2560) Line 154	C++
     	EAThreadTest.exe!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx=0x00000000002e2560) Line 69	C++
     	EAThreadTest.exe!std::_Mutex_base::lock() Line 42	C++
    

    Is it a violation of the C++ standard to attempt to lock a mutex in the constructor for an object with static storage duration? If this is supposed to be allowed then how can I work around the assert in the ConcRT runtime?

Toutes les réponses

  • samedi 14 avril 2012 07:08
     
     

    Just guessing, but it seems like you may be trying to use the Concurrency RT before it has been initialized.  There's a brief discussion on best practices with the runtime:  "Do not create concurrency objects at global scope".

    I'm not sure what the C++ standard guarantees in terms of initializing global objects (something I need to go read about :-), but it would seem a pretty safe bet that initialization is single-threaded, in which case locks aren't needed during global initialization.  So the simplest solution would be to avoid the use of the mutex during global initialization, and only use the mutex for initializations that occur after main has started.

    Cheers,

  • mardi 17 avril 2012 18:29
     
     

    It does seem safe to assume that initialization is single threaded. Unfortunately the code where I was running into this problem is in a memory manager and it doesn't have any straightforward way of telling that it is being called during initialization rather than during normal program operation. 

    I've worked around the problem for now by using a Win32 Critical Section rather than a C++11 mutex but I'm still curious to know what if anything the standard has to say about using an std::mutex during global initialization / pre-main. I've looked through the draft standard sections on mutex and couldn't find anything indicating whether this is supposed to work or not. If the standard doesn't guarantee it then I guess it is something to know to avoid but if it is allowed by the standard it suggests that the VC11 mutex implementation is not strictly standards compliant.

  • mardi 17 avril 2012 22:36
     
      A du code

    Thanks for the feedback,

    We were not able to repro this failure with the following code. can you please share more about your repro?

    Thanks

    Ameen

    struct Test
    {
            std::recursive_mutex m;
            Test()
            {
                   m.lock();
    
                   m.unlock();
            }
    } t;
    


    mameen

  • mardi 17 avril 2012 23:50
     
     Traitée

    On closer inspection it turns out the code in question is using #pragma init_seg to force the global object to be created before any CRT objects are initialized. This is for a custom memory manager that is trying to ensure it hooks all memory allocations for the entire lifetime of the program. This is presumably why it hits the assert where your repro doesn't.

    I can work around the problem as mentioned with a Win32 Critical Section. It doesn't look like this is a general problem with VC11, rather it is a result of some tricky initialization order hacking specific to this memory manager code.