завис и / или segfault при использовании ускорения :: потоки из Matlab, а не при прямом вызове

В чем проблема, на случай, если у людей возникнет похожая проблема: после некоторых обсуждений с поддержкой Mathworks выяснилось, что это конфликт между повышением системы и поставляемыми библиотеками повышения уровня Matlab: когда я скомпилировал с заголовки системного наддува и связанные с (более старыми) библиотеками наддува Matlab, он потерпел неудачу.Когда я скомпилировал и динамически связал с повышением системы, но затем он динамически загрузил библиотеки повышения Matlab, он завис навсегда.

Статическая привязка к системному бусту работает, как и загрузка правильных заголовков для версии буста, с которой поставляется Matlab, и компиляция с ними. Конечно, сборки Matlab для Mac не имеют номеров версий в именах файлов, хотя сборки Linux и предположительно Windows имеют. Для справки R2011b использует повышение 1,44.


У меня есть многопоточный код, который отлично работает при прямой компиляции, но при его вызове из интерфейса Matlab mex возникают сбои и / или взаимоблокировки. Я не знаю, обнаруживает ли другая среда ошибку в моем коде или что, но я не могу понять этого ....

Я использую это на трех конфигурациях компьютеров (хотя есть несколько из коробки CentOS):

  • OSX 10.7, g ++ 4.2, boost 1.48, Matlab R2011a (clang ++ 2.1 также работает в автономном режиме, не пытался заставить mex использовать clang)
  • древний CentOS, g ++ 4.1.2, boost 1.33.1 (отладка, а не отладка), Matlab R2010b
  • , древний CentOS, g ++ 4.1.2, boost 1.40 (отладочные версии не установлены), Matlab R2010b

Вот урезанная версия с таким поведением.

#include 
#include 

#include 
#include 

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

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

    boost::mutex &results_mutex;
    std::vector &results;

    public:

    Worker(boost::mutex &jobs_mutex, std::queue &jobs,
           boost::mutex &results_mutex, std::vector &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 doWork(size_t n) {
    std::vector results;
    results.resize(n);

    boost::mutex jobs_mutex, results_mutex;

    std::queue 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 results = doWork(10);
    for (size_t i = 0; i < results.size(); i++)
        printf("%g ", results[i]);
    printf("\n");
}

Обратите внимание, что при boost 1.48 я получаю такое же поведение, если меняю функтор на стандартную функцию и просто передаю boost :: ref мьютексам / данным в качестве дополнительных аргументов для boost :: резьба . Однако Boost 1.33.1 этого не поддерживает.


Когда я компилирую его напрямую, он всегда работает нормально - я никогда не видел, чтобы он терпел неудачу ни в одной ситуации:

$ 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

При запуске из Matlab я видел много разных вариантов поведения после внесения различных настроек в код и и так далее, хотя никаких изменений, которые на самом деле не имеют для меня никакого смысла. Но вот что я видел с точным кодом выше:

  • В OSX / boost 1.48:
    • Если он связан с повышением варианта выпуска, я получаю segfault при попытке доступа к адресу, близкому к 0, внутри boost :: thread :: start_thread , вызываемого из t1 ] конструктор.
    • Если он связан с ускорением варианта отладки, он навсегда зависает в первом boost :: thread :: join . Я не совсем уверен, но я думаю, что рабочие потоки фактически завершены на этом этапе (не вижу ничего в информационных потоках , очевидно, о них).
  • В CentOS / boost 1.33.1 и 1.40:
    • При ускорении выпуска я получаю segfault в pthread_mutex_lock , который вызывается из boost :: thread :: join на t1 .
    • При ускорении отладки он навсегда зависает в __ lll_lock_wait внутри pthread_mutex_lock в том же месте. Как показано ниже, на этом рабочие потоки завершены.

Я не знаю, как еще что-нибудь сделать с segfaults, поскольку они никогда не возникают, когда у меня есть отладочные символы, которые могут фактически сказать мне, что такое нулевой указатель.

В случае вечного зависания я, кажется, всегда получаю что-то вроде этого, если прохожу через 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();

Это точно выглядит так, будто оба потока завершены до вызова t1.join () . Поэтому я попытался добавить вызов sleep (1) в раздел «выполнение работы» между блокировками; когда я прохожу, потоки завершаются после вызова t1.join () , и он по-прежнему зависает навсегда:

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

Если я перехожу на в doWork функция, results заполняется теми же результатами, что и автономная версия, распечатываемая на этом компьютере, поэтому похоже, что все, что происходит, происходит.

Я понятия не имею, что вызывает либо сбои сегментации, либо безумное зависание, или почему это всегда работает вне Matlab и никогда внутри, или почему это отличается с / без отладочных символов, и я понятия не имею, как чтобы приступить к выяснению этого. Есть какие-нибудь мысли?


По предложению @alanxz, я запустил автономную версию кода с помощью инструментов memcheck, helgrind и DRD от valgrind:

  • В CentOS с использованием valgrind 3.5 ни один из инструментов не выдает неподавляемых ошибок .
  • В OSX с использованием valgrind 3.7:
    • Memcheck не выдает неподавленных ошибок.
    • Helgrind вылетает у меня при запуске с любым двоичным файлом (включая, например, valgrind --tool = helgrind ls ) в OSX, жалуясь на неподдерживаемую инструкцию.
    • DRD выдает более сотни ошибок.

Ошибки DRD для меня довольно непостижимы, и хотя я читал руководство и так далее, я не могу в них разобраться. Вот первая из версии кода, в которой я закомментировал второй рабочий / поток:

Thread 2:
Conflicting load by thread 2 at 0x0004b518 size 8
   at 0x3B837: void boost::call_once(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, boost::disable_if&, boost::detail::thread_move_t > >, boost::thread::dummy*>::type) (thread.hpp:204)
   by 0x100001434: boost::thread::thread >(boost::reference_wrapper, boost::disable_if&, boost::detail::thread_move_t > >, 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&) (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)

Строка 66 - это построение потока, а 72 - это вызов join ; между ними нет ничего, кроме комментариев. Насколько я могу судить, это говорит о гонке между этой частью главного потока и инициализацией рабочего потока ... но я действительно не понимаю, как это возможно?

Остальной вывод DRD здесь ; Я ничего не получаю от этого.

6
задан 16 revs 15 February 2012 в 15:58
поделиться