当重写new操作符时,Std::互斥锁挂起



我们有一个内部内存管理器,用于我们的一个产品。内存管理器覆盖newdelete操作符,并且在单线程应用程序中工作得很好。然而,我现在的任务是使它也能与多线程应用程序一起工作。根据我的理解,下面的伪代码应该工作,但它挂在一个旋转,即使与try_lock()。什么好主意吗?

Update # 1

导致"Access Violation":

#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
   ...   
}

导致线程永远挂起:

#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;
   ...
}

更新# 2

递归互斥锁也会导致线程永远挂起:

#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;
   ...
}

更新# 3

Jonathan Wakely建议我应该尝试unique_lock和/或lock_guard,但锁仍然挂在旋转中。

unique_lock test:

#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 test:

#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;
   ...
}

我认为我的问题是delete是由c++ 11互斥锁库调用时锁定。delete也像这样被覆盖:

/*!
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);
       ...
    }
}

这会导致死锁的情况,我看不到任何好的解决方案,除了使我自己的锁定不产生任何调用newdelete在锁定或解锁。

更新# 4

我已经实现了我自己的自定义递归互斥,它没有调用newdelete,而且,它允许同一个线程进入锁定块。

#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;
   ...
}

更新# 5

尝试了Jonathan Wakely的建议,发现微软对c++ 11互斥锁的实现肯定有问题;如果使用/MTd(多线程调试)编译器标志编译,则会挂起,但如果使用/MDd(多线程调试DLL)编译器标志编译则会正常工作。正如Jonathan正确指出的那样,std::mutex的实现应该是constexpr的。这是我用来测试实现问题的VS 2012 c++代码:

#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;
}

更新# 6

向微软提交bug报告:https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag细节

互斥锁库使用new,并且std::互斥锁在默认情况下是不是递归的(即可重入)。鸡生蛋还是蛋生鸡的问题。

UPDATE正如在下面的注释中指出的那样,使用std::recursive_mutex可能有效。但是经典的c++问题——全局变量的静态初始化顺序没有很好地定义——仍然存在,就像外部访问全局互斥锁的危险一样(最好把它放在匿名命名空间中)。

UPDATE 2您可能过早地将g_systemInitiated转换为true,即在互斥锁有机会完成初始化之前,因此对malloc()的"首次通过"调用永远不会发生。要强制执行此操作,您可以尝试将main()中的赋值替换为调用allocator模块中的初始化函数:

namespace {
    std::recursive_mutex    g_mutex;
    bool                    g_initialized = false;
}
void initialize()
{
    g_mutex.lock();
    g_initialized = true;
    g_mutex.unlock();
}

你所有的例子都是坏的,除了第一个,这是一个非常糟糕的做法,因为没有使用范围锁类型。

如果你的编译器或标准库坏了,这应该可以工作:

#include <mutex>
std::mutex g_mutex;
void *operator new(size_t size)
{
   std::lock_guard<std::mutex> lock(g_mutex);
   ...   
}

最新更新