Como parte de un proyecto estoy escribiendo una función de registrador. Esta función de registrador envía un correo electrónico cuando el programa quiere registrar algo. Dado que sucedió que el servidor SMTP no respondía, he decidido enviar los correos en un hilo separado. Este hilo lee los mensajes de un std :: deque que se llena con la función de registro. El hilo se configura como sigue:Aserción en mutex al usar múltiples hilos y mutexes
while (!boost::this_thread::interruption_requested())
{
EmailItem emailItem;
{
boost::unique_lock<boost::mutex> lock(mMutex);
while (mEmailBuffer.empty())
mCond.wait(lock);
bufferOverflow = mBufferOverflow;
mBufferOverflow = false;
nrOfItems = mEmailBuffer.size();
if (nrOfItems > 0)
{
emailItem = mEmailBuffer.front();
mEmailBuffer.pop_front();
}
}
if (nrOfItems > 0)
{
bool sent = false;
while(!sent)
{
try
{
..... Do something with the message .....
{
boost::this_thread::disable_interruption di;
boost::lock_guard<boost::mutex> lock(mLoggerMutex);
mLogFile << emailItem.mMessage << std::endl;
}
sent = true;
}
catch (const std::exception &e)
{
// Unable to send mail, an exception occurred. Retry sending it after some time
sent = false;
boost::this_thread::sleep(boost::posix_time::seconds(LOG_WAITBEFORE_RETRY));
}
}
}
}
La función log() añade un nuevo mensaje a la cola de doble extremo (mEmailBuffer) de la siguiente manera:
{
boost::lock_guard<boost::mutex> lock(mMutex);
mEmailBuffer.push_back(e);
mCond.notify_one();
}
Cuando las salidas principales del programa, el destructor de la se llama objeto logger. Aquí es donde las cosas van mal, la aplicación se bloquea con un error:
/usr/include/boost/thread/pthread/mutex.hpp:45: boost::mutex::~mutex(): Assertion `!pthread_mutex_destroy(&m)' failed.
El destructor simplemente llama a una interrupción en el hilo y luego se une a ella:
mQueueThread.interrupt();
mQueueThread.join();
En el programa principal, el uso múltiple diferentes clases que hacen uso de boost threading y mutex también, ¿podría esto causar este comportamiento? Al no llamar al destructor del objeto logger, no se producen errores, como ocurre con el uso del objeto logger y sin hacer nada más.
Supongo que estoy haciendo algo muy incorrecto, o hay un error en la biblioteca de subprocesos al usar varios subprocesos divididos en varias clases. ¿Alguien tiene una idea de cuál podría ser el motivo de este error?
EDITAR: Hice como @Andy T propuse y quité el código tanto como sea posible. Eliminé casi todo en la función que se ejecutó en un hilo diferente. El hilo ahora se ve así:
void Vi::Logger::ThreadedQueue()
{
bool bufferOverflow = false;
time_t last_overflow = 0;
unsigned int nrOfItems = 0;
while (!boost::this_thread::interruption_requested())
{
EmailItem emailItem;
// Check for new log entries
{
boost::unique_lock<boost::mutex> lock(mMutex);
while (mEmailBuffer.empty())
mCond.wait(lock);
}
}
}
El problema persiste. Vuelta atrás del problema, sin embargo me mostró algo diferente del código inicial:
#0 0x00007ffff53e9ba5 in raise (sig=<value optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x00007ffff53ed6b0 in abort() at abort.c:92
#2 0x00007ffff53e2a71 in __assert_fail (assertion=0x7ffff7bb6407 "!pthread_mutex_lock(&m)", file=<value optimized out>, line=50, function=0x7ffff7bb7130 "void boost::mutex::lock()") at assert.c:81
#3 0x00007ffff7b930f3 in boost::mutex::lock (this=0x7fffe2c1b0b8) at /usr/include/boost/thread/pthread/mutex.hpp:50
#4 0x00007ffff7b9596c in boost::unique_lock<boost::mutex>::lock (this=0x7fffe48b3b40) at /usr/include/boost/thread/locks.hpp:349
#5 0x00007ffff7b958db in boost::unique_lock<boost::mutex>::unique_lock (this=0x7fffe48b3b40, m_=...) at /usr/include/boost/thread/locks.hpp:227
#6 0x00007ffff6ac2bb7 in Vi::Logger::ThreadedQueue (this=0x7fffe2c1ade0) at /data/repos_ViNotion/stdcomp/Logging/trunk/src/Logger.cpp:198
#7 0x00007ffff6acf2b2 in boost::_mfi::mf0<void, Vi::Logger>::operator() (this=0x7fffe2c1d890, p=0x7fffe2c1ade0) at /usr/include/boost/bind/mem_fn_template.hpp:49
#8 0x00007ffff6acf222 in boost::_bi::list1<boost::_bi::value<Vi::Logger*> >::operator()<boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list0> (this=0x7fffe2c1d8a0, f=..., a=...) at /usr/include/boost/bind/bind.hpp:253
#9 0x00007ffff6acf1bd in boost::_bi::bind_t<void, boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list1<boost::_bi::value<Vi::Logger*> > >::operator() (this=0x7fffe2c1d890) at /usr/include/boost/bind/bind_template.hpp:20
#10 0x00007ffff6aceff2 in boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf0<void, Vi::Logger>, boost::_bi::list1<boost::_bi::value<Vi::Logger*> > > >::run (this=0x7fffe2c1d760)
at /usr/include/boost/thread/detail/thread.hpp:56
#11 0x00007ffff2cc5230 in thread_proxy() from /usr/lib/libboost_thread.so.1.42.0
#12 0x00007ffff4d87971 in start_thread (arg=<value optimized out>) at pthread_create.c:304
#13 0x00007ffff549c92d in clone() at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#14 0x0000000000000000 in ??()
Podría ser posible que mMutex no se desbloquea en la combinación del uso de un unique_lock() y luego interrumpir el hilo?
Sí, uní el hilo antes de salir. Además, hasta donde yo sé, no hay puntos de interrupción en lugares donde un mutex está bloqueado en el hilo. Por lo tanto, no esperaría que el hilo todavía tenga un mutex bloqueado cuando se interrumpa. – Tim
vuelva a verificar una vez más que su mutex (correcto) no se elimina antes de que se una a –
Ambos mutexes son variables miembro de la clase Logger. El destructor de esta clase llama a las funciones 'interrupt()' y 'join()' en el hilo. Los mutexes, por lo tanto, todavía deberían estar vivos cuando el hilo se una, si estoy en lo cierto. – Tim