2012-01-25 7 views
6

Qual è il problema, nel caso in cui le persone hanno un problema simile: dopo alcune discussioni con il supporto di Mathworks, si è rivelato essere un conflitto tra l'amplificatore di sistema e le librerie di boost spedite da Matlab: quando ho compilato le intestazioni di boost di sistema e collegato con le librerie di Matlab boost (più vecchie), ha un segfault. Quando ho compilato e collegato dinamicamente con system boost ma poi ha caricato dinamicamente le librerie di boost Matlab, è rimasto per sempre.hang e/o segfault quando si usa boost :: threads da MATLAB, non quando chiamato direttamente

Il collegamento statico a System Boost funziona, così come il download delle intestazioni corrette per la versione di boost che Matlab fornisce e che compila con quelle. Naturalmente, i build Mac di Matlab non hanno numeri di versione nei loro nomi di file, sebbene lo facciano Linux e presumibilmente le build di Windows. R2011b usa boost 1.44, come riferimento.


ho qualche codice multithread che funziona bene quando è compilato direttamente, ma segfaults e/o deadlock quando si chiama da un'interfaccia Matlab mex. Non so se il diverso ambiente sta rivelando un difetto nel mio codice, o cosa, ma non riesco a capirlo ...

Sto eseguendo questo su tre configurazioni della macchina (anche se ci sono diversi delle scatole CentOS):

  • OSX 10.7, g ++ 4.2, aumentare 1.48, Matlab R2011a (clang ++ 2.1 funziona anche per standalone, non hanno trovato a mex per usare clang)
  • antica CentOS, g ++ 4.1 .2, boost 1.33.1 (debug e non debug), Matlab R2010b
  • CentOS antico, g ++ 4.1.2, boost 1.40 (nessuna versione di debug installata), Matlab R2010b

Ecco una versione ridotta con questo comportamento.

#include <queue> 
#include <vector> 

#include <boost/thread.hpp> 
#include <boost/utility.hpp> 

#ifndef NO_MEX 
#include "mex.h" 
#endif 

class Worker : boost::noncopyable { 
    boost::mutex &jobs_mutex; 
    std::queue<size_t> &jobs; 

    boost::mutex &results_mutex; 
    std::vector<double> &results; 

    public: 

    Worker(boost::mutex &jobs_mutex, std::queue<size_t> &jobs, 
      boost::mutex &results_mutex, std::vector<double> &results) 
     : 
      jobs_mutex(jobs_mutex), jobs(jobs), 
      results_mutex(results_mutex), results(results) 
    {} 

    void operator()() { 
     size_t i; 
     float r; 

     while (true) { 
      // get a job 
      { 
       boost::mutex::scoped_lock lk(jobs_mutex); 
       if (jobs.size() == 0) 
        return; 

       i = jobs.front(); 
       jobs.pop(); 
      } 

      // do some "work" 
      r = rand()/315.612; 

      // write the results 
      { 
       boost::mutex::scoped_lock lk(results_mutex); 
       results[i] = r; 
      } 
     } 
    } 
}; 

std::vector<double> doWork(size_t n) { 
    std::vector<double> results; 
    results.resize(n); 

    boost::mutex jobs_mutex, results_mutex; 

    std::queue<size_t> jobs; 
    for (size_t i = 0; i < n; i++) 
     jobs.push(i); 

    Worker w1(jobs_mutex, jobs, results_mutex, results); 
    boost::thread t1(boost::ref(w1)); 

    Worker w2(jobs_mutex, jobs, results_mutex, results); 
    boost::thread t2(boost::ref(w2)); 

    t1.join(); 
    t2.join(); 

    return results; 
} 

#ifdef NO_MEX 
int main() { 
#else 
void mexFunction(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs) { 
#endif 
    std::vector<double> results = doWork(10); 
    for (size_t i = 0; i < results.size(); i++) 
     printf("%g ", results[i]); 
    printf("\n"); 
} 

Si noti che su impulso 1,48, ottengo lo stesso comportamento se cambio il funtore in una funzione standard e proprio passo boost::ref s ai mutex/dati come argomenti extra per boost::thread. Boost 1.33.1 non supporta questo, però.


Quando compilo direttamente, funziona sempre bene - non ho mai visto fallire in qualsiasi situazione:

$ g++ -o testing testing.cpp -lboost_thread-mt -DNO_MEX 
$ ./testing 
53.2521 895008 5.14128e+06 3.12074e+06 3.62505e+06 1.48984e+06 320100 4.61912e+06 4.62206e+06 6.35983e+06 

esecuzione da Matlab, ho visto un sacco di comportamenti diversi dopo aver apportato modifiche diverse al codice e così via, anche se nessuna modifica ha effettivamente senso per me. Ma ecco quello che ho visto con il codice esatta:

  • su OSX/amplificare 1.48:
    • Se è collegato ad una spinta release-variante, ho un segfault cercando di accedere a un quasi-0 indirizzo interno boost::thread::start_thread, chiamato dal costruttore t1.
    • Se è collegato a un boost di debug-variante, si blocca per sempre nel primo boost::thread::join. Non ne sono del tutto sicuro, ma penso che i thread dei lavoratori abbiano effettivamente completato a questo punto (non si veda nulla in info threads che è ovviamente loro).
  • su CentOS/boost 1.33.1 e 1.40:
    • con boost rilascio, ottengo un segfault in pthread_mutex_lock, essere chiamato dal boost::thread::join su t1.
    • con boost di debug, si blocca per sempre in __lll_lock_wait all'interno pthread_mutex_lock nello stesso luogo. Come mostrato di seguito, i thread di lavoro hanno completato a questo punto.

non so come fare qualcosa di più con i segfaults, dal momento che non hanno mai verificarsi quando ho simboli di debug che in realtà possono dirmi che cosa il puntatore nullo è.

Nel caso appeso per sempre, mi sembra di avere sempre una cosa del genere se sto passando attraverso in GDB:

99  Worker w1(jobs_mutex, jobs, results_mutex, results); 
(gdb) 
100  boost::thread t1(boost::ref(w1)); 
(gdb) 
[New Thread 0x47814940 (LWP 19390)] 
102  Worker w2(jobs_mutex, jobs, results_mutex, results); 
(gdb) 
103  boost::thread t2(boost::ref(w2)); 
(gdb) 
[Thread 0x47814940 (LWP 19390) exited] 
[New Thread 0x48215940 (LWP 19391)] 
[Thread 0x48215940 (LWP 19391) exited] 
105  t1.join(); 

che sicuramente assomiglia a entrambi i thread sono completi prima della chiamata a t1.join(). Così ho provato ad aggiungere una chiamata sleep(1) nella sezione "doing work" tra i blocchi; quando sto passando attraverso, l'uscita discussioni dopo la chiamata a t1.join() e si blocca ancora per sempre:

106  t1.join(); 
(gdb) 
[Thread 0x47814940 (LWP 20255) exited] 
[Thread 0x48215940 (LWP 20256) exited] 
# still hanging 

Se up fuori alla funzione doWork, results è popolata con gli stessi risultati che la versione standalone stampe su questa macchina, quindi sembra che tutto ciò che sta attraversando.

Non ho idea di cosa stia causando né i segfaults né le pazzesche impiccagioni, o perché funzioni sempre fuori da Matlab e mai dentro, o perché sia ​​diverso con/senza simboli di debug, e non ho idea come procedere nel capire questo. qualche idea?


A @ di alanxz suggerimento, ho eseguito la versione standalone del codice sotto strumenti memcheck, Helgrind e DRD di valgrind:

  • su CentOS utilizzando valgrind 3.5, nessuno degli strumenti fornisce alcuna errori non soppressi.
  • Su OSX utilizzando valgrind 3.7:
    • Memcheck non dà alcun errore non soppresso.
    • Helgrind blocca per me quando eseguito su qualsiasi binario (compresi ad esempio valgrind --tool=helgrind ls) su OSX, lamentano un'istruzione non supportato.
    • DRD dà oltre un centinaio di errori.

Gli errori DRD sono piuttosto imperscrutabile per me, e anche se ho letto il manuale e così via, non riesco a dare un senso a loro. Ecco il primo, su una versione del codice in cui ho commentato la seconda operaio/thread:

Thread 2: 
Conflicting load by thread 2 at 0x0004b518 size 8 
    at 0x3B837: void boost::call_once<void (*)()>(boost::once_flag&, void (*)()) (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib) 
    by 0x2BCD4: boost::detail::set_current_thread_data(boost::detail::thread_data_base*) (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib) 
    by 0x2BA62: thread_proxy (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib) 
    by 0x2D88BE: _pthread_start (in /usr/lib/system/libsystem_c.dylib) 
    by 0x2DBB74: thread_start (in /usr/lib/system/libsystem_c.dylib) 
Allocation context: Data section of r/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib 
Other segment start (thread 1) 
    at 0x41B4DE: __bsdthread_create (in /usr/lib/system/libsystem_kernel.dylib) 
    by 0x2B959: boost::thread::start_thread() (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib) 
    by 0x100001B54: boost::thread::thread<boost::reference_wrapper<Worker> >(boost::reference_wrapper<Worker>, boost::disable_if<boost::is_convertible<boost::reference_wrapper<Worker>&, boost::detail::thread_move_t<boost::reference_wrapper<Worker> > >, boost::thread::dummy*>::type) (thread.hpp:204) 
    by 0x100001434: boost::thread::thread<boost::reference_wrapper<Worker> >(boost::reference_wrapper<Worker>, boost::disable_if<boost::is_convertible<boost::reference_wrapper<Worker>&, boost::detail::thread_move_t<boost::reference_wrapper<Worker> > >, boost::thread::dummy*>::type) (thread.hpp:201) 
    by 0x100000B50: doWork(unsigned long) (testing.cpp:66) 
    by 0x100000CE1: main (testing.cpp:82) 
Other segment end (thread 1) 
    at 0x41BBCA: __psynch_cvwait (in /usr/lib/system/libsystem_kernel.dylib) 
    by 0x3C0C3: boost::condition_variable::wait(boost::unique_lock<boost::mutex>&) (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib) 
    by 0x2D28A: boost::thread::join() (in /usr/local/boost/boost_1_48_0/stage/lib/libboost_thread-mt-d.dylib) 
    by 0x100000B61: doWork(unsigned long) (testing.cpp:72) 
    by 0x100000CE1: main (testing.cpp:82) 

linea 66 è la costruzione del filo, e il 72 è la chiamata join; non c'è nient'altro che commenti in mezzo. Per quanto posso dire, questo sta dicendo che c'è una corsa tra quella parte del thread principale e l'inizializzazione del thread di lavoro ...ma non capisco davvero come sia possibile?

Il resto dell'uscita da DRD is here; Non ne ricaverò niente.

+1

Hai provato a eseguirlo sotto valgrind, [helgrind] (http://valgrind.org/docs/manual/hg-manual.html) o [DRD] (http://valgrind.org/docs/manual /drd-manual.html)? Questo potrebbe rivelare alcuni indizi su cosa sta succedendo. – alanxz

+0

@alanxz Grazie per il suggerimento, non ero a conoscenza di helgrind/DRD. Ho aggiunto alcuni dettagli su ciò che dicono alla domanda. Ottengo errori DRD su OSX, ma non ho idea di cosa significano, nonostante la lettura del manuale e così via ... – Dougal

+0

Qualcuno ha provato a configurare @rpath in ambiente Linux simile? Attualmente ho lo stesso problema, penso che il Messico dovrebbe fare il corretto isolamento delle sue dipendenze. – Raffi

risposta

1

Sei sicuro che sia il caso più semplice che seggaults e/o blocchi? Se i risultati di DRD indicano una condizione di competizione tra la costruzione e l'unione del filo, sembra che il tuo codice potrebbe non essere in errore (soprattutto dal momento che non utilizzi alcuna funzione specifica mex, ma l'esecuzione solo sotto mex attiva l'errore).

Forse provare solo questa versione:

#include <boost/thread.hpp> 

void doNothing() { return; } 

void doWork() { 
    boost::thread t1(doNothing); 
    t1.join(); 
} 

#ifdef NO_MEX 
int main() { 
#else 
#include "mex.h" 
void mexFunction(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs) { 
#endif 
    doWork(); 
} 

Questo sicuramente non dovrebbe segmentation fault o appendere sia sotto mex o compilato direttamente - quindi, se lo fa, non è il bug, e se così non fosse, forse puoi progressivamente chiudere la distanza tra la tua versione e questa per trovare l'aggiunta che causa il bug.

+0

Sì, anche quella versione segfaults. Immagino che mex e boost i thread non lavorino insieme. Tempo di segnalarlo come un bug e porta a pthreads, immagino .... – Dougal

0

C'è un punto di errore nel codice: Quando un thread è in ritardo di più di 2 secondi, la chiamata timed_lock nel costruttore serratura può time out, il mutex è non acquisito, e si accede alla struttura protetta Comunque. Se si utilizzano i mutex temporizzati, è necessario verificare se il blocco ha effettivamente bloccato il mutex o semplicemente scaduto. Questo può essere verificato chiamando il metodo owns_lock() dei blocchi.

Qui non vedo alcuna motivazione per i mutex temporizzati, e si menziona "dopo aver eliminato il thread temporizzato", ma sospetto ancora che questo bug di mutex timeout sia in errore qui. Questo errore si verifica ancora quando si sostituisce timed_mutex con semplice mutex?

+0

Inizialmente usavo il semplice 'mutex'; la roba 'timed_mutex' è stata aggiunta dopo aver visto il comportamento del deadlock. Per qualche ragione pensavo che "timed_mutex" abbia lanciato un'eccezione se non avesse acquisito il blocco, anche se immagino di non sapere perché l'ho pensato. Inoltre, quando segfaults, accade immediatamente - sicuramente non qualcosa che sta accadendo dopo 2 secondi. – Dougal

+0

Per essere espliciti: sì, ottengo ancora segfault/blocco quando sostituisco 'timed_mutex' con un semplice' mutex' (e rimuovo l'argomento time e così via, ovviamente). Ora vedo una consistenza nel problema tra la mia macchina OSX Boost 1.48 e la macchina CentOS Boost 1.33.1 che non sembravo avere prima, anche se ... indagherò un po 'più a fondo e poi modificherò la domanda . – Dougal

+0

Ho risolto il problema e modificato la domanda. Sembra che il blocco non sia stato il problema, dal momento che segfaults prima di arrivare su OSX + nuovo boost, e su CentOS + vecchio boost i thread completati con successo (almeno quando sto eseguendo il debugging) ma si blocca o si blocca quando viene chiesto di aderire. – Dougal

Problemi correlati