2012-08-31 13 views
5

Cuando comencé a aprender la herramienta valgrind (helgrind), me encontré con un problema tan difícil que no pude abordar.por qué valgrind (helgrind) genera "Razas de datos posibles" en caso de que se invoque una función virtual en mi estructura de hilos

En simple, se crea una clase de subproceso definida por el usuario con una función virtual a la que se llamaría mediante la rutina de entrada del subproceso. Si este es el caso, helgrind informará Posible-data-race. Pero después de simplemente omitir la palabra clave virtual, no se informará ningún error. ¿Cómo es que esto sucede de esta manera? ¿Hay algún problema con mi código? ¿O hay una solución alternativa?

De ahora en adelante es la sencilla aplicación de subprocesos que demuestra tal problema, incluyendo cpp, Makefile y mensajes que ayudan a generar informes.

/* 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

Bueno, el acceso a 's_v' no parece estar sincronizado ... –

Respuesta

2

En un caso, el vptr está escrito, en el otro, es leído. Ambos sin ningún tipo de bloqueo. Helgrind no puede saber si hay otros medios en su programa que hacen que esta condición sea imposible de suceder en dos hilos simultáneamente, por lo que lo marca. Si puede garantizar que el objeto no se destruye mientras que en otro hilo alguien intenta invocar una función sobre él, entonces puede generar una supresión para esto.

+0

parece que uno debe llamar a pthread_join antes de destruir dicha instancia de subprocesos como sugiere Michael Burr, o suprimirla. – user1638062

+0

@ user1638062: Esa también es una alternativa, pero aún debes suprimirla si desconectas ese hilo. Además, probablemente debas hacer que tu dtor sea virtual (parece que esa clase está planeada para ser usada polimórficamente), y luego te encontrarás con los mismos problemas ya que no puedes unir() antes de que se ejecute el dtor de la clase derivada. Para evitar algunos problemas con los hilos, recomendaría probar boost :: thread o std :: thread ... – PlasmaHH

2

No sé si este es el motivo de la queja de Helgrind, pero usted tiene un problema grave en su programa. Crea un hilo, pasando un puntero a una instancia local thread_s (s_v en main()).

Sin embargo, pronto se volverá main() sin ningún tipo de sincronización con hilo - no hay nada para asegurar que s_v está todavía vivo cuando la función del hilo routine() se apodera del puntero y lo utiliza para llamar pV->routine().

A ver si la adición de la siguiente después de la llamada pthread_create() Helgrind impide quejarse:

pthread_join(s_v.th, NULL); 

En realidad, mirando más de cerca la salida de Helgrind, esto es casi seguro que retire la denuncia de Helgrind, ya que el registro está apuntando a la thread_s destructor como un participante en la carrera de datos.

+0

pthread_join (s_v.th, NULL); – user1638062

+0

pthread_join (s_v.th, NULL); Sí, también lo intenté cuando probé esa aplicación de muestra. Funciona si se llama pthread_join justo antes de que s_v sea destruido. Pero todavía espero otra solución, porque no me gusta agregar stop-function antes de la destrucción, si tales subprocesos de archivo se mantienen en un contenedor donde también existen objetos que no sean de subprocesos. – user1638062

+0

La clave es que necesita mantener vivo el objeto 'thread_s' y que sea válido por lo menos mientras sea el hilo de ejecución. Usé 'pthread_join()' como una solución simple para este ejemplo simple, pero hay varias maneras de hacerlo (aunque generalmente terminan con un 'pthread_join()' siendo utilizado en algún momento, simplemente no directamente después de crearlo la amenaza). –

Cuestiones relacionadas