2012-08-31 5 views
5

Quando ho iniziato a imparare lo strumento valgrind (helgrind), mi sono imbattuto in un problema così delicato che non sono riuscito a risolvere.perché valgrind (helgrind) genera "Possible Data Races" nel caso in cui la funzione virtuale venga chiamata sulla mia struttura di thread

In modo semplice, una classe thread definita dall'utente viene creata con una funzione virtuale che verrebbe chiamata dalla routine di immissione del thread. Se questo è il caso, helgrind segnalerà Possible-data-race. Ma dopo aver omesso semplicemente la parola chiave virtuale, non verrebbero mai segnalati errori di questo tipo. Come mai questo accade in questo modo? Qualcosa non va nel mio codice? O c'è una soluzione?

Qui di seguito è la semplice applicazione a thread a dimostrare tale problema, inclusi cpp, Makefile e messaggi che helgrind segnala.

/* main.cpp */ 
#include <memory.h> 
#include <pthread.h> 

class thread_s { 
public: 
    pthread_t  th; 
    thread_s(void); 
    ~thread_s(void); 
    virtual void* routine(); /* if omit virtual, no error would be generated */ 
    void stop(void); 
}; 
static void* routine(void*); 
int main(int, const char*[]) 
{ 
    thread_s s_v; 
    pthread_create(&s_v.th, 0, routine, &s_v); 
    return 0; 
} 
static void* routine(void* arg) 
{ 
    thread_s *pV = reinterpret_cast<thread_s*>(arg); 
    pV->routine(); 
    return 0; 
} 
void* thread_s::routine(void) 
{ 
    return 0; 
} 
thread_s::thread_s(void) 
{ 
    th = 0; 
} 
thread_s::~thread_s(void) 
{ 
    stop(); 
} 
void thread_s::stop(void) 
{ 
    void *v = 0; 
    pthread_join(th, &v); 
} 

=======================================

/* Makefile */ 
all: main test_helgrind 

main: main.cpp 
     g++ -o main main.cpp \ 
     -g -Wall -O0 \ 
     -lpthread 

test_helgrind: 
     valgrind \ 
       --tool=helgrind \ 
       ./main 

clean: 
     rm -f main 

.PHONY: clean 

=======================================

g++ -o main main.cpp \ 
     -g -Wall -O0 \ 
     -lpthread 
valgrind \ 
       --tool=helgrind \ 
       ./main 
==7477== Helgrind, a thread error detector 
==7477== Copyright (C) 2007-2010, and GNU GPL'd, by OpenWorks LLP et al. 
==7477== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info 
==7477== Command: ./main 
==7477== 
==7477== Thread #1 is the program's root thread 
==7477== 
==7477== Thread #2 was created 
==7477== at 0x4259728: clone (clone.S:111) 
==7477== by 0x40484B5: [email protected]@GLIBC_2.1 (createthread.c:256) 
==7477== by 0x4026E2D: pthread_create_WRK (hg_intercepts.c:257) 
==7477== by 0x4026F8B: [email protected]* (hg_intercepts.c:288) 
==7477== by 0x8048560: main (main.cpp:18) 
==7477== 
==7477== Possible data race during write of size 4 at 0xbeab24c8 by thread #1 
==7477== at 0x80485C9: thread_s::~thread_s() (main.cpp:35) 
==7477== by 0x8048571: main (main.cpp:17) 
==7477== This conflicts with a previous read of size 4 by thread #2 
==7477== at 0x804858B: routine(void*) (main.cpp:24) 
==7477== by 0x4026F60: mythread_wrapper (hg_intercepts.c:221) 
==7477== by 0x4047E98: start_thread (pthread_create.c:304) 
==7477== by 0x425973D: clone (clone.S:130) 
==7477== 
==7477== 
==7477== For counts of detected and suppressed errors, rerun with: -v 
==7477== Use --history-level=approx or =none to gain increased speed, at 
==7477== the cost of reduced accuracy of conflicting-access information 
==7477== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 1 from 1) 
+0

Bene, l'accesso a 's_v' non sembra essere sincronizzato .. –

risposta

2

In un caso il vptr è scritto, nell'altro è letto. Entrambi senza blocco. Helgrind non può sapere se ci sono altri mezzi nel tuo programma che rendono impossibile che questa condizione si verifichi in due thread simultaneamente, quindi lo contrassegna. Se è possibile garantire che l'oggetto non venga distrutto mentre in un altro thread qualcuno tenta di richiamare una funzione su di esso, è possibile generare una soppressione per questo.

+0

sembra che uno debba chiamare pthread_join prima di distruggere tale istanza di thread come suggerito da Michael Burr o sopprimerlo. – user1638062

+0

@ user1638062: anche questa è un'alternativa, ma è necessario sopprimerla se si scollega quel thread. Probabilmente dovresti anche rendere virtuale il tuo dtor (sembra che la classe sia pianificata per essere usata polimorficamente), e poi ti imbatterai negli stessi problemi dato che non puoi join() prima che venga eseguito il dtor della classe derivata. Per evitare alcuni problemi con i thread, ti consiglio di provare boost :: thread o std :: thread ... – PlasmaHH

2

Non so se questo è il motivo del reclamo di helgrind, ma avete un problema serio nel vostro programma. Si crea un thread passando un puntatore a un'istanza locale thread_s (s_v in main()).

Tuttavia, main() tornerà presto senza alcun tipo di sincronizzazione con filo - non c'è nulla per garantire che s_v è ancora vivo quando la funzione di filo routine() si impossessa del puntatore e lo utilizza per chiamare pV->routine().

Vedere se aggiungendo la seguente dopo la chiamata pthread_create() Helgrind impedisce di lamentarsi:

pthread_join(s_v.th, NULL); 

In realtà, guardando più da vicino all'uscita Helgrind, questo sarà quasi certamente rimuovere la denuncia di Helgrind, dal momento che il registro sia rivolta alla thread_s distruttore come un partecipante nella corsa dati.

+0

pthread_join (s_v.th, NULL); – user1638062

+0

pthread_join (s_v.th, NULL); Sì, ho provato anche in questo modo quando ho provato quella app di esempio. Funziona se pthread_join viene chiamato appena prima che s_v venga distrutto. Ma non vedo l'ora di un'altra soluzione, perché non mi piace aggiungere la funzione stop prima di distruggere, se tali istanze di thread sono conservate in un contenitore in cui esistono anche oggetti diversi dal thread. – user1638062

+0

La cosa fondamentale è che è necessario mantenere attivo l'oggetto 'thread_s' valido almeno per il tempo del thread in esecuzione. Ho usato 'pthread_join()' come una soluzione semplice per questo semplice esempio, ma ci sono un certo numero di modi per farlo (anche se di solito finiscono con l'uso di 'pthread_join()', non direttamente dopo la creazione il filo). –