diff --git a/include/libtorrent/broadcast_socket.hpp b/include/libtorrent/broadcast_socket.hpp index ebcb28968..1e3811f7f 100644 --- a/include/libtorrent/broadcast_socket.hpp +++ b/include/libtorrent/broadcast_socket.hpp @@ -117,6 +117,9 @@ namespace libtorrent void open_multicast_socket(io_service& ios, address const& addr , bool loopback, error_code& ec); + // if we're aborting, destruct the handler and return true + bool maybe_abort(); + // these sockets are used to // join the multicast group (on each interface) // and receive multicast messages @@ -128,6 +131,18 @@ namespace libtorrent std::list m_unicast_sockets; udp::endpoint m_multicast_endpoint; receive_handler_t m_on_receive; + + // the number of outstanding async operations + // we have on these sockets. The m_on_receive + // handler may not be destructed until this reaches + // 0, since it may be holding references to + // the broadcast_socket itself. + int m_outstanding_operations; + // when set to true, we're trying to shut down + // don't initiate new operations and once the + // outstanding counter reaches 0, destruct + // the handler object + bool m_abort; }; } diff --git a/src/broadcast_socket.cpp b/src/broadcast_socket.cpp index b23e48ce4..9ad9d22fc 100644 --- a/src/broadcast_socket.cpp +++ b/src/broadcast_socket.cpp @@ -217,6 +217,8 @@ namespace libtorrent , bool loopback) : m_multicast_endpoint(multicast_endpoint) , m_on_receive(handler) + , m_outstanding_operations(0) + , m_abort(false) { TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address())); @@ -279,6 +281,7 @@ namespace libtorrent #endif s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer)) , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); + ++m_outstanding_operations; } void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr @@ -305,6 +308,7 @@ namespace libtorrent #endif s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer)) , se.remote, boost::bind(&broadcast_socket::on_receive, this, &se, _1, _2)); + ++m_outstanding_operations; } void broadcast_socket::send(char const* buffer, int size, error_code& ec, int flags) @@ -365,14 +369,31 @@ namespace libtorrent #if defined TORRENT_ASIO_DEBUGGING complete_async("broadcast_socket::on_receive"); #endif - if (ec || bytes_transferred == 0 || !m_on_receive) return; + TORRENT_ASSERT(m_outstanding_operations > 0); + --m_outstanding_operations; + + if (ec || bytes_transferred == 0 || !m_on_receive) + { + maybe_abort(); + return; + } m_on_receive(s->remote, s->buffer, bytes_transferred); + + if (maybe_abort()) return; if (!s->socket) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("broadcast_socket::on_receive"); #endif s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer)) , s->remote, boost::bind(&broadcast_socket::on_receive, this, s, _1, _2)); + ++m_outstanding_operations; + } + + bool broadcast_socket::maybe_abort() + { + if (m_abort && m_outstanding_operations == 0) + m_on_receive.clear(); + return m_abort; } void broadcast_socket::close() @@ -380,7 +401,8 @@ namespace libtorrent std::for_each(m_sockets.begin(), m_sockets.end(), boost::bind(&socket_entry::close, _1)); std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), boost::bind(&socket_entry::close, _1)); - m_on_receive.clear(); + m_abort = true; + maybe_abort(); } } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 6269ef381..37d3df62d 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1598,9 +1598,9 @@ namespace aux { m_i2p_conn.close(ec); #endif m_queued_for_checking.clear(); - if (m_lsd) m_lsd->close(); - if (m_upnp) m_upnp->close(); - if (m_natpmp) m_natpmp->close(); + stop_lsd(); + stop_upnp(); + stop_natpmp(); #ifndef TORRENT_DISABLE_DHT if (m_dht) {