O bloqueio std :: mutex trava ao substituir o novo operador

Temos um gerenciador de memória interna que usamos com um de nossos produtos. O gerenciador de memória substitui onew edelete operadores, e funciona bem em apliações single-threaded. No entanto, agora estou encarregado de fazê-lo funcionar com aplicativos multi-threaded também. Pelo que entendi, o seguinte pseudocódigo deve funcionar, mas ele gira, mesmo comtry_lock(). Alguma ideia?

Atualização 1

Causa "violação de acesso":

#include <mutex>

std::mutex g_mutex;

/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
   g_mutex.lock(); // Access violation exception
   ...   
}

Faz com que o fio fique pendurado para sempre em um giro:

#include <mutex>

std::mutex g_mutex;
bool g_systemInitiated = false;


/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
   if (g_systemInitiated == false) return malloc(size);
   g_mutex.lock(); // Thread hangs forever here. g_mutex.try_lock() also hangs
   ...   
}

int main(int argc, const char* argv[])
{
   // Tell the new() operator that the system has initiated
   g_systemInitiated = true;
   ...
}

Atualização 2

Um mutex recursivo também faz com que o encadeamento permaneça indefinidamente em um giro:

#include <mutex>

std::recursive_mutex g_mutex;
bool g_systemInitiated = false;


/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
   if (g_systemInitiated == false) return malloc(size);
   g_mutex.lock(); // Thread hangs forever here. g_mutex.try_lock() also hangs
   ...   
}

int main(int argc, const char* argv[])
{
   // Tell the new() operator that the system has initiated
   g_systemInitiated = true;
   ...
}

Atualização 3

Jonathan Wakely sugeriu que eu deveria tentarunique_lock e / oulock_guard, mas a fechadura continua em um giro.

unique_lock teste:

#include <mutex>

std::mutex g_mutex;
std::unique_lock<std::mutex> g_lock1(g_mutex, std::defer_lock);
bool g_systemInitiated = false;

/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
   if (g_systemInitiated == false) return malloc(size);
   g_lock1.lock(); // Thread hangs forever here the first time it is called
   ...   
}

int main(int argc, const char* argv[])
{
   // Tell the new() operator that the system has initiated
   g_systemInitiated = true;
   ...
}

lock_guard teste:

#include <mutex>

std::recursive_mutex g_mutex;
bool g_systemInitiated = false;


/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
   if (g_systemInitiated == false) return malloc(size);
   std::lock_guard<std::mutex> g_lock_guard1(g_mutex); // Thread hangs forever here the first time it is called
   ...   
}

int main(int argc, const char* argv[])
{
   // Tell the new() operator that the system has initiated
   g_systemInitiated = true;
   ...
}

Eu acho que meu problema é quedelete é chamado pela biblioteca mutex C ++ 11 ao bloquear.delete também é substituído da seguinte forma:

/*!
\brief Overrides the Standard C++ new operator
\param p [in] The pointer to memory to free
*/
void operator delete(void *p)
{
    if (g_systemInitiated == false)
    {
       free(p); 
    }
    else
    {
       std::lock_guard<std::mutex> g_lock_guard1(g_mutex);
       ...
    }
}

Isso faz com que deadlocking situação que eu não posso ver qualquer boa solução, exceto para fazer meu próprio bloqueio que não gera quaisquer chamadas paranew oudelete enquanto bloqueia ou desbloqueia.

Atualização 4

Eu implementei o meu próprio mutex recursivo personalizado que não tem chamadas paranew oudeletetambém permite que o mesmo thread entre em um bloco bloqueado.

#include <thread>

std::thread::id g_lockedByThread;
bool g_isLocked = false;
bool g_systemInitiated = false;

/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
   if (g_systemInitiated == false) return malloc(size);

   while (g_isLocked && g_lockedByThread != std::this_thread::get_id());
   g_isLocked = true; // Atomic operation
   g_lockedByThread = std::this_thread::get_id();
   ...   
   g_isLocked = false;
}

/*!
\brief Overrides the Standard C++ new operator
\param p [in] The pointer to memory to free
*/
void operator delete(void *p)
{
    if (g_systemInitiated == false)
    {
       free(p); 
    }
    else
    {
       while (g_isLocked && g_lockedByThread != std::this_thread::get_id());
       g_isLocked = true; // Atomic operation
       g_lockedByThread = std::this_thread::get_id();
       ...   
       g_isLocked = false;
    }
}

int main(int argc, const char* argv[])
{
   // Tell the new() operator that the system has initiated
   g_systemInitiated = true;
   ...
}

Atualização 5

Tentei a sugestão de Jonathan Wakely, e descobri que definitivamente parece haver algo errado com a implementação da Microsoft em C ++ 11 Mutexes; seu exemplo trava se compilado com o/MTd (Debug Multi-threaded), mas funciona bem se compilado com o/MDd Sinalizador de compilador (Multi-threaded Debug DLL). Como Jonathan apontou corretamentestd::mutex implementações devem serconstexpré. Aqui está o código do VS 2012 C ++ que usei para testar o problema de implementação:

#include "stdafx.h"

#include <mutex>
#include <iostream>

bool g_systemInitiated = false;
std::mutex g_mutex;

void *operator new(size_t size)
{
    if (g_systemInitiated == false) return malloc(size);
    std::lock_guard<std::mutex> lock(g_mutex);
    std::cout << "Inside new() critical section" << std::endl;
    // <-- Memory manager would be called here, dummy call to malloc() in stead
    return malloc(size);
}

void operator delete(void *p)
{
    if (g_systemInitiated == false) free(p); 
    else
    {
        std::lock_guard<std::mutex> lock(g_mutex);
        std::cout << "Inside delete() critical section" << std::endl;
        // <-- Memory manager would be called here, dummy call to free() in stead
        free(p);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    g_systemInitiated = true;

    char *test = new char[100];
    std::cout << "Allocated" << std::endl;
    delete test;
    std::cout << "Deleted" << std::endl;

    return 0;
}

Atualização 6

Enviou um relatório de bug para a Microsoft:https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag#details

questionAnswers(2)

yourAnswerToTheQuestion