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() измениться, так что ваша проблема должна быть что-то другое.

Другие вопросы по тегам