2012-01-25 8 views
6

Co było problemem, na wypadek gdyby ludzie mieli podobny problem: po kilku dyskusjach ze wsparciem Mathworks, okazało się, że jest to konflikt pomiędzy wzmocnieniem systemu a dostarczonymi przez Matlaba bibliotekami boost: kiedy skompilowałem je z nagłówkami doładowania systemu i połączono z (starszymi) bibliotekami doładowania Matlab, uległo to uszkodzeniu. Kiedy skompilowałem i dynamicznie związałem się z podnoszeniem systemu, ale potem dynamicznie ładowałem biblioteki doładowania Matlaba, to było to na zawsze.zawiesić się i/lub segfault podczas używania boost :: threads z MATLAB, a nie gdy zostanie wywołany bezpośrednio

Statyczne połączenie z działaniem systemu działa, podobnie jak pobieranie poprawnych nagłówków dla wersji boost, którą Matlab dostarcza i kompiluje z tymi. Oczywiście kompilacje Maca w Matlabie nie mają numerów wersji w nazwach plików, chociaż Linux i podobno kompilacje Windowsa. R2011b używa doładowania 1.44, dla odniesienia.


Mam wielowątkowych kod, który działa prawidłowo, gdy jest kompilowany bezpośrednio, ale zwraca błąd i/lub zakleszczenia, gdy jest wywoływana z Matlab mex interfejsu. Nie wiem, czy inne środowisko ujawnia błąd w moim kodzie, czy co, ale nie mogę tego rozgryźć ...

Używam tego na trzech konfiguracjach maszyn (choć jest ich kilka pól CentOS):

  • OSX 10.7, g ++ 4.2, zwiększyć 1.48, Matlab R2011a (dzyń ++ 2.1 działa również na autonomiczny, nie próbowali uzyskać mex w użyciu szczęk)
  • starożytną CentOS, g ++ 4.1 .2, zwiększenie 1.33.1 (debugowanie i nie debugowanie), Matlab R2010b
  • starożytny CentOS, g ++ 4.1.2, doładowanie 1.40 (brak zainstalowanych wersji debugowania), Matlab R2010b

Oto krótka wersja tego zachowania.

#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"); 
} 

Zauważ, że na impuls 1,48, mam ten sam problem, jeśli zmienię funktor do standardowej funkcji i po prostu przekazać boost::ref S do muteksy/danych jako dodatkowe argumenty do boost::thread. Boost 1.33.1 nie obsługuje tego.


Kiedy skompilować go bezpośrednio, to zawsze działa poprawnie - Nigdy nie widziałem to nie w każdej sytuacji:

$ 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 

biegnącego z Matlab, widziałem dużo różnych zachowań po wprowadzeniu różnych poprawek do kodu i tak dalej, ale żadnych zmian, które faktycznie mają dla mnie sens. Ale oto co widziałem z dokładnym powyższym kodzie:

  • na OSX/pobudzenia 1.48:
    • Jeśli jest to związane z pobudzenia uwalniania wariantem, otrzymuję segfault próbuje uzyskać dostęp do niemal 0 adres wewnątrz boost::thread::start_thread, wywoływany z konstruktora t1.
    • Jeśli jest powiązany z podbiciem z wariantem debugowania, zawsze zawiesza się na pierwszym boost::thread::join. Nie jestem do końca pewien, ale myślę, że wątki robocze zostały już w tym momencie zakończone (nie widzę niczego w tym, co to jest info threads).
  • CentOS/przypominającego 1.33.1 i 1.40:
    • Z pobudzenia uwalniania, otrzymuję segfault w pthread_mutex_lock, nazywany od boost::thread::join na t1.
    • Po wzmocnieniu debugowania, zawiesza się na zawsze w __lll_lock_wait wewnątrz pthread_mutex_lock w tym samym miejscu. Jak pokazano poniżej, wątki robocze zakończyły się w tym momencie.

nie wiem jak to zrobić nic więcej z naruszenia ochrony pamięci, ponieważ nigdy nie występuje, gdy mam symbole debugowania, że ​​faktycznie może mi powiedzieć, co jest wskaźnikiem NULL.

W wiszące-forever przypadku, wydaje mi się, aby zawsze uzyskać coś takiego, jakbym przechodzeniu w 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(); 

To wątpienia wygląda obu wątków są kompletne przed wywołaniem t1.join(). Próbowałem więc dodać wywołanie sleep(1) w sekcji "praca" między blokadami; kiedy jestem przechodzeniu, wyjście nici po wywołaniu t1.join() i nadal wisi na zawsze:

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

Gdybym up się do funkcji doWork, results jest wypełniana z tych samych wyników, że samodzielna wersja drukuje na ta maszyna, więc wygląda na to, co się dzieje.

Nie mam pojęcia, co powoduje któryś z segfaultów lub zwariowanego zawieszania się, lub dlaczego to zawsze działa poza Matlabem i nigdy w środku, lub dlaczego jest inaczej z/bez symboli debugowania, i nie mam pojęcia jak postępować w ustalaniu tego. jakieś pomysły?


AT @ alanxz sugestią, Zabrakło mi autonomiczną wersję kodu pod narzędzi memcheck, Helgrind i DRD Valgrind za:

  • CentOS korzystających valgrind 3.5, żadne z narzędzi dać dowolny niezablokowane błędy.
  • Na OSX przy użyciu valgrind 3.7:
    • W memcheck nie występują żadne niezablokowane błędy.
    • Helgrind zawiesza się dla mnie po uruchomieniu dowolnego systemu plików binarnych (w tym np. valgrind --tool=helgrind ls) w systemie OSX, narzekając na nieobsługiwane instrukcje.
    • DRD daje ponad sto błędów.

błędów DRD są całkiem niezbadane do mnie, i chociaż czytałem instrukcji i tak dalej, nie mogę żadnego sensu z nich. Oto pierwszy z nich, w wersji kodu gdzie zakomentowanym drugi robotnik/wątku:

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) 

Linia 66 jest budowa nitki, a 72 jest wezwanie join; nie ma nic oprócz komentarzy pomiędzy. O ile mogę powiedzieć, chodzi o wyścig pomiędzy tą częścią wątku głównego a inicjacją wątku roboczego ...ale tak naprawdę nie rozumiem, jak to jest możliwe?

Reszta wyjściu z DRD is here; Nic z tego nie wyciągnę.

+1

Czy próbowałeś uruchomić go pod valgrind [Helgrind] (http://valgrind.org/docs/manual/hg-manual.html) lub [DRD] (http://valgrind.org/docs/manual /drd-manual.html)? To może ujawnić pewne wskazówki na temat tego, co się dzieje. – alanxz

+0

@alanxz Dzięki za sugestię, nie byłem świadomy helgrind/DRD. Dodałem kilka szczegółów na temat tego, co mówią na to pytanie. Dostaję błędy DRD na OSX, ale nie mam pojęcia, co one oznaczają, pomimo przeczytania instrukcji itd. – Dougal

+0

Czy ktoś próbował ustawić ścieżkę @rpath w środowisku podobnym do Linuksa? Obecnie mam ten sam problem, myślę, że mex powinien zrobić właściwą izolację swoich zależności. – Raffi

Odpowiedz

1

Czy jesteś pewien, że to najprostszy przypadek, że naruszenia ochrony pamięci i/lub zawiesza się? Jeżeli wyniki z DRD wskazują stan wyścigu tylko między budową gwintu i łączenia, że ​​brzmi to jak Twój kod nie może być winy (zwłaszcza, że ​​w rzeczywistości nie używać żadnych cech specyficznych dla mex, ale po prostu działa pod mex wyzwala błąd).

Może spróbuj tylko tej wersji:

#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(); 
} 

To na pewno nie powinno się wysypać lub zawiesić albo pod mex lub sporządzane bezpośrednio - tak, jeśli to robi, to nie jest twój błąd, a jeśli nie, może możesz stopniowo zmniejszać odległość między wersją a tą, aby znaleźć dodatek powodujący błąd.

+0

Tak, ta wersja również ulega uszkodzeniu. Domyślam się, że wątki mex i boost nie działają razem. Czas zgłosić to jako błąd i port do pthreads, chyba .... – Dougal

0

Jest punkt awarii w kodzie: Kiedy każdy wątek jest opóźniony o więcej niż 2 sekundy, wywołanie w konstruktorze blokady timed_lock mogą limit czasu, mutex jest nie nabyte, i uzyskać dostęp do chronionego strukturę tak czy inaczej. Jeśli użyjesz timex mutexów, będziesz musiał sprawdzić, czy blokada rzeczywiście zablokowała muteks lub tylko przekroczyła limit czasu. Można to sprawdzić, wywołując metodę blokady owns_lock().

Nie widzę tutaj żadnej motywacji dla muteksów czasowych i wspominasz o "po wyjęciu czasowych wątków", ale wciąż podejrzewam, że błąd związany z czasem błędu mutex jest tutaj wadliwy. Czy ten błąd nadal występuje po zastąpieniu timed_mutex zwykłym mutex?

+0

Pierwotnie użyłem zwykłego 'mutex'; rzeczy 'timed_mutex' zostały dodane po zobaczeniu zakleszczenia. Z jakiegoś powodu myślałem, że 'timed_mutex' wyrzucił wyjątek, jeśli nie uzyskał blokady, choć wydaje mi się, że nie wiem, dlaczego tak myślałem. Ponadto, gdy się kruszy, dzieje się to natychmiast - zdecydowanie nie jest to coś, co dzieje się po 2 sekundach. – Dougal

+0

być jawne: tak, ja wciąż się naruszenia ochrony pamięci/zawiesza się podczas I zastąpić 'timed_mutex' ze zwykłego' mutex' (i usunąć argumentu czas i tak dalej, oczywiście). Teraz widzę konsekwencję problemu między moim komputerem OSX Boost 1.48 a maszyną CentOS Boost 1.33.1, którego wcześniej nie miałem, jednak ... zbada to nieco dokładniej, a następnie zmodyfikuje pytanie . – Dougal

+0

Naprawiłem ten problem i zredagowałem pytanie. Wygląda na to, że blokowanie nie stanowiło problemu, ponieważ odskakuje, zanim dotrze do niego w systemie OSX + nowy impuls, a na starym CentOS + zwiększy liczbę pomyślnie ukończonych wątków (przynajmniej podczas ich debugowania), ale zawiesza się lub zawiesza. gdy zostaniesz poproszony o dołączenie. – Dougal

Powiązane problemy