std::list падает при обратном вызове libev
У меня есть функция обратного вызова libev write, которая проверяет ожидающие данные для отправки клиенту. Ожидание буфера данных выглядит как
struct PendingData{
unsigned short data_size;
char data[4096];
};
typedef std::list<PendingData*> PendingBuf;
class Client{
private:
int sock;
PendingBuf data_list;
public:
ev::io cl_io;
void write_cb(ev::io &watcher, int events);
};
и функция обратного вызова проверяет, есть ли какие-либо данные в контейнере следующим образом:
void Client::write_cb(ev::io &watcher, int events){
PendingData* pd = NULL;
int ires = 0;
if(!data_list.empty()){
pd = data_list.front();
ires = send(sock, pd->data, pd->data_size, 0);
if(ires == pd->data_size){
delete pd;
data_list.pop_front();
return;
}
// .... additional checking here
}
}
программа вылетает с ошибкой сегментации
if(!data_list.empty())
а иногда на
pd = data_list.front();
во втором случае empty() возвращает false, но отладчик показывает, что в списке нет элементов данных
он работает в отдельном потоке (буфер читается и записывается из одного и того же потока). Я также пытался переместить это в основной поток, не запуская никаких дополнительных потоков, но с тем же эффектом.
ОС Ubuntu 12.04, компилятор g++ 4.6, в моем проекте также включен C++0x
2 ответа
Может быть, вам нужно просто использовать блокировки (мьютексы) при доступе к std::list как
// near list
class Client {
/// ...
PendingBuf data_list;
std::mutex list_lock;
/// ...
}
void Client::write_cb(ev::io &watcher, int events){
std::lock_guard<std::mutex> lock(list_lock);
if(!data_list.empty()){
/// ...
}
и при записи в этот список также используйте блокировки. Также есть возможность использовать блокировки чтения и записи. Блокировка записи всегда исключает все остальные (чтение и запись). Блокировки чтения могут быть получены много раз в одно и то же время для одновременного чтения.
libstdC++'s std::list
несовместим между режимами C++98 и C++11/ C++0x. См. http://gcc.gnu.org/wiki/Cxx11AbiCompatibility
Причина в том, что в C++98 это не требовалось для std::list::size()
быть O(1) и libstdC++ реализовал его как O(n). В C++ 11 он должен быть O(1), поэтому дополнительный size_t
участник присутствует в std::list
в режиме C++ 11, который изменяет размер объекта.
Если ваше приложение использует C++ 11, то в целом все библиотеки, на которые вы ссылаетесь, должны делать то же самое.
Изменить: извинения, я только что понял, что вы сказали GCC 4.6, который не имеет std::list::size()
измениться, так что ваша проблема должна быть что-то другое.