2012-01-25 21 views
6

¿Cuál era el problema, en caso de que las personas tengan un problema similar: después de algunas discusiones con el soporte de Mathworks, resultó ser un conflicto entre el impulso del sistema y las bibliotecas de impulso enviadas de Matlab: cuando compilé con cabeceras de impulso del sistema y las conecté con bibliotecas de Matlab boost (más antiguas), fallaron. Cuando lo compilé y lo vinculé dinámicamente con el impulso del sistema, pero luego cargó dinámicamente las bibliotecas de Matlab boost, quedó colgado para siempre.colgar y/o segfault al usar boost :: threads desde matlab, no cuando se llama directamente

La vinculación estática con el sistema aumenta, al igual que la descarga de los encabezados correctos para la versión de refuerzo que se incluye con Matlab y la compilación de esos. Por supuesto, las compilaciones de Mac de Matlab no tienen números de versión en sus nombres de archivo, aunque las compilaciones de Linux y supuestamente de Windows sí. R2011b usa boost 1.44, como referencia.


que tienen algo de código multiproceso que funciona bien cuando se compila directamente, sino segfaults y/o bloqueos cuando se llama desde una interfaz mex Matlab. No sé si el entorno diferente está revelando un defecto en mi código, o qué, pero no puedo resolverlo ...

Estoy ejecutando esto en tres configuraciones de máquina (aunque hay varias de las cajas de CentOS):

  • OSX 10.7, g ++ 4.2, impulsar 1.48, Matlab R2011a (sonido metálico ++ 2.1 también funciona para independiente, no han tratado de conseguir mex utilizar sonido metálico)
  • antigua CentOS, g ++ 4.1 .2, impulso 1.33.1 (depuración y no depuración), Matlab R2010b
  • antiguo CentOS, g ++ 4.1.2, impulso 1.40 (no hay versiones de depuración instaladas), Matlab R2010b

Aquí hay una versión reducida con este comportamiento.

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

Tenga en cuenta que el impulso 1.48, consigo el mismo comportamiento si cambio el funtor en una función estándar y sólo tiene que pasar boost::ref s a las exclusiones mutuas/datos como argumentos adicionales a boost::thread. Boost 1.33.1 no es compatible con esto, sin embargo.


Cuando compilo directamente, siempre funciona muy bien - que nunca he visto fallar en cualquier situación:

$ 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 

Ejecución de Matlab, he visto un montón de diferentes comportamientos después de hacer diferentes ajustes al código y demás, aunque no hay cambios que realmente tengan sentido para mí. Pero esto es lo que he visto con el código exacto arriba:

  • en OSX/impulsar 1.48:
    • Si está vinculada a un impulso de liberación variante, me sale un error de segmentación intentar acceder a un cercano 0 dirección dentro de boost::thread::start_thread, siendo llamado desde el constructor t1.
    • Si está vinculado a un impulso de depuración-variante, se cuelga para siempre en el primer boost::thread::join. No estoy del todo seguro, pero creo que los hilos de trabajo se han completado en este momento (no veo nada en info threads que obviamente sean ellos).
  • en CentOS/refuerzo 1.33.1 y 1.40:
    • Con impulso de liberación, me sale un error de segmentación en pthread_mutex_lock, siendo llamados desde el boost::thread::join en t1.
    • Con refuerzo de depuración, se cuelga para siempre en __lll_lock_wait dentro de pthread_mutex_lock en el mismo lugar. Como se muestra a continuación, los hilos de trabajo se han completado en este punto.

yo no sé hacer nada más con las violaciones de segmento, ya que nunca ocurrirá cuando tenga símbolos de depuración que realmente me puede decir lo que el puntero nulo es.

En el caso colgante-para siempre, me parece conseguir siempre algo como esto si estoy pasando a través de 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(); 

Eso seguro que se ve como ambos hilos están completos antes de la llamada a t1.join(). Así que intenté agregar una llamada sleep(1) en la sección "hacer trabajo" entre los bloqueos; cuando estoy pasando a través de la salida de las discusiones después de la llamada a t1.join() y todavía cuelga siempre:

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

Si up a la función doWork, results se rellena con los mismos resultados que la versión impresiones independientes en esta máquina, por lo que parece que todo lo que está pasando.

No tengo idea de qué está causando ninguno de los segfaults o el colgamiento loco, o por qué es que siempre funciona fuera de Matlab y nunca dentro, o por qué es diferente con/sin símbolos de depuración, y no tengo idea cómo proceder para resolver esto. ¿Alguna idea?


A sugerencia de @ alanxz, me he encontrado la versión independiente del código bajo herramientas memcheck, Helgrind, y DRD de valgrind:

  • en CentOS utilizando valgrind 3.5, ninguna de las herramientas de dar cualquier errores no suprimidos
  • En OSX utilizando valgrind 3.7:
    • Memcheck no proporciona ningún error no suprimido.
    • Helgrind se cuelga cuando se ejecuta en cualquier sistema binario (incluido, por ejemplo, valgrind --tool=helgrind ls) en OSX, quejándose de una instrucción no admitida.
    • DRD ofrece más de cien errores.

Los errores DRD son bastante inescrutable para mí, y aunque he leído el manual y así sucesivamente, no puede hacer ningún sentido. Aquí está la primera de ellas, en una versión del código en el que comentaba el segundo trabajador/hilo:

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) 

Línea 66 es la construcción de la rosca, y el 72 es la llamada join; no hay más que comentarios intermedios. Por lo que puedo decir, esto está diciendo que hay una carrera entre esa parte del hilo maestro y la inicialización del hilo de trabajo ...pero realmente no entiendo cómo es posible?

El resto de la salida de DRD is here; No estoy obteniendo nada de eso.

+1

¿Ha intentado ejecutarlo en valgrind, [helgrind] (http://valgrind.org/docs/manual/hg-manual.html) o [DRD] (http://valgrind.org/docs/manual /drd-manual.html)? Eso podría revelar algunas pistas sobre qué está pasando. – alanxz

+0

@alanxz Gracias por la sugerencia, no estaba al tanto de helgrind/DRD. He agregado algunos detalles sobre lo que dicen a la pregunta. Recibo errores DRD en OSX, pero no tengo idea de lo que significan, a pesar de leer el manual, etc. – Dougal

+0

¿Alguien intentó configurar @rpath en un entorno similar a Linux? Actualmente tengo el mismo problema, estoy pensando que el mex debería hacer el aislamiento adecuado de sus dependencias. – Raffi

Respuesta

1

¿Está seguro de que es el caso más simple que segfaults y/o cuelga? Si los resultados de DRD indican una condición de carrera solo entre la construcción de la secuencia y la unión, parece que tu código podría no tener la culpa (especialmente porque no utilizas ninguna característica específica de mex, pero solo ejecutarlo bajo mex desencadena el error)

Tal vez intente simplemente esta versión:

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

Esto definitivamente no debe segfault o colgar ya sea bajo mex o compilado directamente - por lo que si lo hace, no es su error, y si no lo hace, tal vez puede cerrar progresivamente la distancia entre su versión y esta para encontrar la adición causante de errores.

+0

Sí, esa versión segfaults también. Creo que los hilos mex y boost simplemente no funcionan juntos. Es hora de informarlo como un error y puerto a pthreads, supongo ... – Dougal

0

Hay un punto de fallo en el código: Cuando cualquier tema tiene un retraso de más de 2 segundos, la llamada timed_lock en el constructor de bloqueo puede tiempo de espera, el mutex es no adquiridos, y acceder a la estructura protegida de todas formas. Si usa mutexes temporizados, deberá probar si el bloqueo en realidad bloqueó el mutex o simplemente agotó el tiempo de espera. Esto se puede verificar llamando al método 'owns_lock() de las cerraduras'.

No veo ninguna motivación para los mutex de tiempo programado, y mencionas "después de sacar el hilo cronometrado", pero sigo sospechando que este error de timeout mutex tiene la culpa aquí. ¿Esta falla aún ocurre cuando reemplaza timed_mutex con mutex normal?

+0

Originalmente utilicé 'mutex' simple; las cosas 'timed_mutex' se agregaron después de ver el comportamiento de interbloqueo. Por alguna razón, pensé que 'timed_mutex' arrojó una excepción si no adquiría el bloqueo, aunque supongo que no sé por qué pensé eso. Además, cuando se segmenta, ocurre de inmediato, definitivamente no es algo que sucede después de 2 segundos. – Dougal

+0

Para ser explícito: sí, todavía obtengo segfaults/cuelga cuando reemplazo 'timed_mutex' con un' mutex' simple (y elimino el argumento de tiempo y así sucesivamente, obviamente). Ahora veo una consistencia en el problema entre mi máquina OSX Boost 1.48 y la máquina CentOS Boost 1.33.1 que no parecía tener antes, sin embargo ... investigaremos eso un poco más a fondo y luego editaremos la pregunta . – Dougal

+0

Solucioné ese problema y edité la pregunta. Parece que el bloqueo no fue el problema, ya que segfaults antes de que llegue en OSX + nuevo impulso, y en CentOS + viejo impulso los hilos se completan con éxito (al menos cuando los estoy depurando) pero se bloquea o cuelga cuando se le pide unirse – Dougal

Cuestiones relacionadas