Abbiamo un gestore di memoria interno che usiamo con uno dei nostri prodotti. Il gestore della memoria sovrascrive gli operatori new
e delete
e funziona perfettamente nelle applet a thread singolo. Tuttavia, ora ho il compito di farlo funzionare anche con applicazioni multi-thread. Per quanto ne so, il seguente pseudo codice dovrebbe funzionare, ma si blocca su un giro, anche con try_lock()
. Qualche idea?std :: Il blocco mutex si blocca quando si sostituisce il nuovo operatore
Aggiornamento # 1
cause "Violazione di accesso":
#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
...
}
cause filo per appendere per sempre in uno spin:
#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;
...
}
Aggiornamento # 2
A mutex ricorsiva causa anche il filo per appendere per sempre in uno spin:
#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;
...
}
Update # 3
Jonathan Wakely ha suggerito che dovrei provare unique_lock
e/o lock_guard
, ma la serratura si blocca ancora in un rotazione.
unique_lock
prova:
#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
prova:
#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;
...
}
Penso che il mio problema è che delete
viene chiamato dalla libreria mutex C++ 11 quando il blocco. delete
, viene sostituito in questo modo:
/*!
\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);
...
}
}
Questo fa sì deadlocking situazione che non riesco a vedere alcuna buona soluzione per tranne per fare il mio blocco che non depongono le uova le chiamate al new
o delete
mentre il blocco o lo sblocco.
Aggiornamento # 4
Ho implementato il mio mutex ricorsiva proprio personalizzato che non hanno chiamate a new
o delete
, anche, consente lo stesso filo di entrare in un blocco bloccato.
#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;
...
}
Update # 5
provato suggerimento Jonathan Wakely, e ha scoperto che sicuramente sembra che ci sia qualcosa di sbagliato con l'implementazione Microsoft di C++ 11 mutex; il suo esempio si blocca se compilato con il flag del compilatore /MTd
(debug multithreading), ma funziona correttamente se compilato con il flag del compilatore /MDd
(DLL a thread multipli). Come Jonathan ha giustamente sottolineato std::mutex
le implementazioni dovrebbero essere constexpr
.Ecco il codice VS 2012 C++ che ho usato per testare il problema di implementazione:
#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;
}
Update # 6
Inserito un bug report a Microsoft: https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag#details
non vedo come questo possa creare un collegamento, in cui si definisce la tua g_mutex? ci sono due dichiarazioni, dovresti avere esternamente una di queste. –
Cosa fanno effettivamente gli operatori nuovi ed eliminati (oltre all'assegnazione/liberazione della memoria)? –
@claptrap È pseudo codice. –