Test delle condizioni di gara multi-thread

52

Leggendo i commenti a questa risposta , in particolare:

Just because you can't write a test doesn't mean it's not broken. Undefined behaviour which usually happens to work as expected (C and C++ are full of that), race conditions, potential reordering due to a weak memory model... – CodesInChaos 7 hours ago

@CodesInChaos if it cant be reproduced then the code written to 'fix' cant be tested either. And putting untested code into live is a worse crime in my opinion – RhysW 5 hours ago

... mi chiedo se ci siano buoni modi generali per innescare in modo molto raro i problemi di produzione causati dalle condizioni di gara nel caso di test.

    
posta Dan Neely 25.04.2013 - 16:25
fonte

6 risposte

80

Dopo aver lavorato in questo folle business dal 1978, dopo aver trascorso quasi tutto questo tempo nell'informatica embedded in tempo reale, lavorando su multitasking, multithreading, sistemi multi-any, a volte con più processori fisici, avendo inseguito più della mia fiera quota di condizioni di gara, la mia opinione ponderata è che la risposta alla tua domanda è abbastanza semplice.

No.

Non c'è un buon modo generale per innescare una condizione di competizione nei test.

La tua SOLA speranza è di progettarli completamente fuori dal tuo sistema.

Quando e se trovi che qualcun altro ne ha imbottito uno, dovresti metterlo fuori gioco da un formicaio, quindi riprogettare per eliminarlo. Dopo aver progettato il suo passo falso (pronunciato f *** up) dal tuo sistema, puoi andare a liberarlo dalle formiche. (Se le formiche lo hanno già consumato, lasciando solo le ossa, metti un cartello che dice "Questo è quello che succede alle persone che mettono le condizioni della razza nel progetto XYZ!" E LASCIARTI DI LUI.)

    
risposta data 25.04.2013 - 16:42
fonte
16

Se ti trovi nella catena di strumenti ms. La ricerca di Ms ha creato uno strumento che imporrà nuovi intervalli per ogni corsa e può ricreare esecuzioni fallite denominate scacchi .

qui è un video che lo mostra in uso.

    
risposta data 25.04.2013 - 17:21
fonte
15

Lo strumento migliore che conosco per questo tipo di problemi è un'estensione di Valgrind chiamato Helgrind .

Fondamentalmente Valgrind simula un processore virtuale e gira il tuo binario (non modificato) su di esso, in modo che possa controllare ogni singolo accesso alla memoria. Utilizzando questo framework, il sistema di watch Helgrind chiama per inferire quando un accesso a una variabile condivisa non è adeguatamente protetto da un meccanismo di mutua esclusione. In questo modo è in grado di rilevare una condizione di competizione teorica anche se non è mai avvenuta.

Intel vende uno strumento molto simile chiamato Intel Inspector .

Questi strumenti danno ottimi risultati ma il tuo programma sarà notevolmente più lento durante l'analisi.

    
risposta data 25.04.2013 - 21:25
fonte
6

L'esposizione di un bug multi-thread richiede di forzare diversi thread di esecuzione per eseguire i propri passi in un particolare ordine interfogliato. Di solito questo è difficile da fare senza debugging manuale o manipolazione del codice per ottenere una sorta di "handle" per controllare questo interleaving. Ma cambiare codice che si comporta in modo imprevedibile spesso influisce sull'imprevedibilità, quindi è difficile automatizzarlo.

Un bel trucco è descritto da Jaroslav Tulach in Progettazione dell'API pratica : se nel codice sono presenti istruzioni di registrazione sotto domanda, manipola il consumatore di quelle istruzioni di registrazione (ad esempio uno pseudo-terminale iniettato) in modo che accetti i singoli messaggi di registro in un particolare ordine in base al loro contenuto. Ciò consente di controllare l'interleaving dei passaggi in thread diversi senza dover aggiungere nulla al codice di produzione che non è già presente.

    
risposta data 25.04.2013 - 16:37
fonte
6

Non c'è modo di essere assolutamente sicuri che vari tipi di comportamento indefinito (in particolare le condizioni di gara) non esistano.

Tuttavia, esistono numerosi strumenti che mostrano un buon numero di tali situazioni. Potresti essere in grado di dimostrare che al momento esiste un problema con tali strumenti, anche se non puoi dimostrare che la tua correzione è valida.

Alcuni strumenti interessanti per questo scopo:

Valgrind è un controllore di memoria. Trova perdite di memoria, letture di memoria non inizializzata, uso di puntatori penzolanti e accessi fuori limite.

Helgrind è un controllo di sicurezza del thread. Trova condizioni di gara.

Entrambi funzionano con strumentazione dinamica, cioè prendono il tuo programma così com'è e lo eseguono in un ambiente virtualizzato. Questo li rende non invadenti, ma lenti.

UBSan è un controllo del comportamento non definito. Trova vari casi di comportamento non definito di C e C ++, come overflow integer, spostamenti fuori intervallo e cose simili.

MSan è un controllore di memoria. Ha obiettivi simili a quelli di Valgrind.

TSan è un controllo di sicurezza del thread. Ha obiettivi simili a quelli di Helgrind.

Questi tre sono integrati nel compilatore Clang e generano il codice in fase di compilazione. Ciò significa che devi integrarli nel tuo processo di compilazione (in particolare, devi compilare con Clang), il che rende molto più difficile l'impostazione iniziale rispetto a * grind, ma d'altra parte hanno un sovraccarico di runtime molto più basso.

Tutti gli strumenti che ho elencato funzionano su Linux e alcuni su MacOS. Non credo che lavori su Windows siano ancora affidabili.

    
risposta data 26.04.2013 - 19:23
fonte
0

Sembra che la maggior parte delle risposte qui sbagli questa domanda come "come faccio a rilevare automaticamente le condizioni di gara?" quando la domanda è veramente "come faccio a riprodurre le condizioni di gara nei test quando li trovo?"

Il modo per farlo è introdurre la sincronizzazione nel codice che viene utilizzata solo per i test. Ad esempio, se si verifica una condizione di competizione quando si verifica l'evento X tra l'evento A e l'evento B, quindi per testare l'applicazione, scrivere un codice che attenda che l'evento X si verifichi dopo l'evento A. Probabilmente avrai bisogno di un modo per i tuoi test per parlare con la tua applicazione per dirglielo ("hey sto testando questa cosa, quindi aspetta questo evento in questa posizione").

Sto usando node.js e mongo, dove alcune azioni implicano la creazione di dati coerenti in più raccolte. In questi casi, i miei test unitari invieranno una chiamata all'applicazione per dirgli "imposta un'attesa per l'evento X" e, una volta che l'applicazione l'ha configurata, il test per l'evento X verrà eseguito e i test diranno successivamente l'applicazione ("ho finito con l'attesa dell'evento X"), quindi il resto dei test verrà eseguito normalmente.

La risposta qui spiega questo tipo di cose in dettaglio nel contesto di python: link

    
risposta data 14.05.2015 - 22:06
fonte

Leggi altre domande sui tag