2013-01-14 10 views
5

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

+1

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. –

+0

Cosa fanno effettivamente gli operatori nuovi ed eliminati (oltre all'assegnazione/liberazione della memoria)? –

+0

@claptrap È pseudo codice. –

risposta

1

La biblioteca mutex utilizza new, e std :: mutex è non ricorsivo (cioè rientrante) per impostazione predefinita. Un problema di pollo e uova.

UPDATE Come è stato sottolineato nei commenti seguenti, l'utilizzo di std :: recursive_mutex potrebbe funzionare. Ma la classica C++ problema dell'ordine di inizializzazione statico di variabili globali che sono resti non ben definito, così come il pericolo di accesso esterno al mutex globale (meglio metterlo all'interno di un namespace anonimo.)

UPDATE 2 È potrebbe passare g_systemInitiated a true troppo presto, cioè prima che il mutex abbia avuto la possibilità di completare l'inizializzazione, e quindi la chiamata "first time-through" a malloc() non avviene mai. Per forzare questo, si potrebbe provare a sostituire l'assegnazione in main() con una chiamata a una funzione di inizializzazione nel modulo allocatore:

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

Ok. Quindi, c'è un modo per aggirare questo? Un modo per bloccare un operatore 'new' sottoposto a override? –

+0

Non che io sappia per std :: mutex. Ma con la libreria pthreads di livello inferiore, è possibile (staticamente) inizializzare un pthreads_mutex_t globale con PTHREAD_RECURSIVE_MUTEX_INITIALIZER, che potrebbe aggirare il problema. Ma si perderebbe la comodità di codificare con l'API std :: thread, ovviamente. – arayq2

+0

bene lui può usare std :: recursive_mutex invece –

0

Tutti i tuoi esempi sono rotti, tranne il primo, che è solo molto male pratica per non utilizzare un tipo di blocco scoped.

Questo dovrebbe funzionare, se non lo fa il compilatore o libreria standard è rotto:

#include <mutex> 

std::mutex g_mutex; 

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

Sembra decisamente che ci sia qualcosa di sbagliato nell'implementazione di C++ 11 Mutex di Microsoft; il tuo esempio si blocca id compilo con il flag del compilatore '/ MTd' (Multi-threaded Debug), ma funziona bene se compilo con il flag del compilatore'/MDd' (Multi-threaded Debug DLL). Aggiungerò un aggiornamento alla mia domanda. –

Problemi correlati