2013-01-14 6 views
5

当社の製品で使用する内部メモリマネージャがあります。メモリマネージャは、newdelete演算子をオーバーライドし、シングルスレッドアプリケーションで正常に動作します。しかし、私は今もマルチスレッドアプリケーションでも動作させることを任されています。私の理解では、次の擬似コードはうまくいくはずですが、それはtry_lock()でさえスピンします。何か案は?新しい演算子をオーバーライドするとstd :: mutexロックがハングします

更新#1

原因 "アクセス違反":

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

ジョナサンWakelyは、私がunique_lockおよび/またはlock_guardを試してみてください示唆したが、ロックがまだ中にハングスピン。

unique_lockテスト:

#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テスト:

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

これは私がロックまたはロック解除しながら、newまたはdeleteへの呼び出しを生成しません。私自身のロックを作る以外のために何か良い解決策を見ることができないデッドロック状況が発生します。

アップデート#4

私はまた、それがロックされたブロックを入力するように同じスレッドを可能にする、newまたはdeleteへの呼び出しを持っていない私自身のカスタム再帰的ミューテックスを実装しました。

#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

ジョナサンWakelyの提案を試みたが、間違いなくC++ 11のミューテックスのMicrosoftの実装に問題があると思われることがわかりました。彼の例は、/MTd(マルチスレッドデバッグ)コンパイラフラグでコンパイルされた場合にハングしますが、/MDd(マルチスレッドデバッグDLL)コンパイラフラグでコンパイルすると正常に動作します。ジョナサンが正しく指摘したようにstd::mutexの実装はconstexprであるはずです。

#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

:ここで私は実装の問題をテストするために使用VS 2012 C++のコードである https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag#details

+1

を私はどこにあなたのg_mutexを定義するか、それがリンクすることができますどのように表示されないのですか? 2つの宣言がある、あなたはそのうちの1つの前にexternを持つべきです。 –

+0

新しい演算子と削除演算子は実際に何をしますか(メモリの割り当て/解放を除いて)? –

+0

@claptrap擬似コードです。 –

答えて

1

がmutexライブラリは、newを使用していますそして、std :: mutexが、デフォルトではない再帰的(すなわちリエントラント)です。鶏と卵の問題

UPDATE以下のコメントで指摘されているように、std :: recursive_mutexを使用すると動作する可能性があります。しかし、グローバルなミューテックスへの外部アクセスの危険性(匿名の名前空間の中に置くのが最善です)のように、グローバルの静的初期化の順序の古典的なC++の問題は残っています。ミューテックスは、その初期化を完了する機会があった、とそうmalloc()「第1回スルー」コールが起こることはありませんつまり前に、早すぎるtrueにg_systemInitiatedを切り替えることができます。これを強制するには、アロケータモジュール内の初期化関数を呼び出して)(メインに割り当てを交換してみてください:

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

それで、これを回避する方法はありますか?オーバーライドされた 'new'演算子をロックする方法は? –

+0

私はstd :: mutexについて知っているわけではありません。しかし、下位レベルのpthreadライブラリでは、PTHREAD_RECURSIVE_MUTEX_INITIALIZERを使用してグローバルpthreads_mutex_tを(静的に)初期化することができます。しかし、もちろん、std :: thread APIを使ってコーディングの利便性を失うことになります。 – arayq2

+0

よく彼はstd :: recursive_mutexを代わりに使うことができます –

0

あなたのすべての例は、単に非常に悪い最初のものを除いて、壊れていますスコープロックタイプを使用しない場合の練習。それはあなたのコンパイラや標準ライブラリが壊れていない場合

これは、動作するはずです:

#include <mutex> 

std::mutex g_mutex; 

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

確かに、MicrosoftのC++ 11 Mutexesの実装には何か問題があるようです。あなたの例は、 '/ MTd'(マルチスレッドデバッグ)コンパイラフラグでコンパイルしたidをハングしますが、'/MDd'(マルチスレッドデバッグDLL)コンパイラフラグでコンパイルするとうまく動作します。私は私の質問に更新を加えます。 –

関連する問題