Ошибка в деструкторе сокета boost

317
26 ноября 2016, 18:56

Используется следующая модель абстракции:

//SHP_*** обозначены shared_ptr<***>
class A
{
    std::vector<SHP_B> vecB;
    boost::asio::io_service io;
}
class B
{
   B(...){socket = std::make_shared<Socket>(...);}
   ~B(){socket->s.close();}
   private: SHP_Socket socket;
}
class Socket
{
    Socket(...){s.open(...); s.set_option(...); s.bind(...);}
    boost::asio::ip::udp::socket s;
}

Все эти классы и их реализация лежат в разных файлах. Все прекрасно собирается, работает, инициализируется, сокеты шлют данные. В vecB добавляются и удаляются элементы... Пока не вызову деструктор класса А. При этом, в vecB должно содержаться (и содержится, проверил) 2 элемента SHP_B.

Тогда возникает ошибка доступа к памяти: Unhandled exception at 0x77BE57DA (ntdll.dll) in Conference.exe: 0xC0000005: Access violation reading location 0xFEEEFEF6. и дебагер кидает в файл: ...\boost\asio\detail\impl\win_iocp_socket_service_base.ipp

void win_iocp_socket_service_base::destroy(
    win_iocp_socket_service_base::base_implementation_type& impl)
{
  close_for_destruction(impl);
  // Remove implementation from linked list of all implementations.
  boost::asio::detail::mutex::scoped_lock lock(mutex_);
  if (impl_list_ == &impl) // <- дебагер указывает на эту строчку
    impl_list_ = impl.next_;
  if (impl.prev_)
    impl.prev_->next_ = impl.next_;
  if (impl.next_)
    impl.next_->prev_= impl.prev_;
  impl.next_ = 0;
  impl.prev_ = 0;
}

Я так понимаю, что ошибка вызвана попыткой удаления уже удаленного сокета или попыткой удаления открытого сокета(что вряд ли), но почему? Закрытие сокета я вызываю только в ~B(), который после исполнения должен вызвать и деструктор класса Socket, в котором вызывается деструктор для boost::asio::ip::udp::socket s., пробовал закрывать вызовом вручную и удаление элементов из vecB по одному перед вызовом деструктора по умолчанию для класса А (ведь до того как мне надо удалить объект из A нормально работало...). Пробовал перенести закрытие сокета в ~Socket() - не помогло.

Из-за чего возникла эта проблема и как ее решить?

Answer 1

Как выяснилось в комментариях проблема в том, что объект типа boost::asio::io_service уничтожается раньше чем последний созданный на нем сокет.

Чтобы избежать этой ситуации можно создать пользовательский деструктор с правильным порядком очистки, что Вы и сделали. А можно просто поменять порядок членов:

class A
{
    boost::asio::io_service io;
    std::vector<SHP_B> vecB;
};

В таком случае, автоматически созданный деструктор будет сначала удалять vecB, а потом уже io. Правда в этом случае надо иметь в виду, что т.к. используется shared_ptr сокет может формально существовать ещё где-то. Если его существование в другим местах не предполагается - имеет смысл использовать другой тип умного указателя, например unique_ptr.

Answer 2

Проблема в том, что ваш io_service уничтожается раньше, чем уничтожается сокет. Продлить время жизни можно двумя способами:

  1. Вынести io_service наружу. Хорошим местом для создания io_service будет локальная переменная в функции main или в какой-нибудь подобной. Или вообще глобальная/статическая.

  2. Создавать io_service через shared_ptr, сохраняя копию в каждом классе B.

READ ALSO
Перегрузка оператор С++

Перегрузка оператор С++

Такой вопросЯ перегружаю операторы для работы с вектором, который состоит из трек точек

261
Ошибки SIGABRT и с sysmalloc

Ошибки SIGABRT и с sysmalloc

Пишу программу по двоичным деревьям

281