diff --git a/include/libtorrent/udp_socket.hpp b/include/libtorrent/udp_socket.hpp index 79b301b49..f26726ed6 100644 --- a/include/libtorrent/udp_socket.hpp +++ b/include/libtorrent/udp_socket.hpp @@ -64,6 +64,10 @@ namespace libtorrent void set_proxy_settings(proxy_settings const& ps); proxy_settings const& get_proxy_settings() { return m_proxy_settings; } +#ifndef NDEBUG + ~udp_socket() { m_magic = 0; } +#endif + private: callback_t m_callback; @@ -94,6 +98,7 @@ namespace libtorrent char m_v4_buf[1600]; char m_v6_buf[1600]; int m_bind_port; + uint8_t m_outstanding; tcp::socket m_socks5_sock; int m_connection_ticket; @@ -103,6 +108,9 @@ namespace libtorrent char m_tmp_buf[100]; bool m_tunnel_packets; udp::endpoint m_proxy_addr; +#ifndef NDEBUG + int m_magic; +#endif }; } diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index 3a5c00892..8f0aefb9f 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -18,20 +18,33 @@ udp_socket::udp_socket(asio::io_service& ios, udp_socket::callback_t const& c , m_ipv4_sock(ios) , m_ipv6_sock(ios) , m_bind_port(0) + , m_outstanding(0) , m_socks5_sock(ios) , m_connection_ticket(-1) , m_cc(cc) , m_resolver(ios) , m_tunnel_packets(false) { +#ifndef NDEBUG + m_magic = 0x1337; +#endif } +#ifndef NDEBUG + #define CHECK_MAGIC check_magic_ cm_(m_magic) + struct check_magic_ + { + check_magic_(int& m_): m(m_) { TORRENT_ASSERT(m == 0x1337); } + ~check_magic_() { TORRENT_ASSERT(m == 0x1337); } + int& m; + }; +#else + #define CHECK_MAGIC (void) +#endif + void udp_socket::send(udp::endpoint const& ep, char const* p, int len, error_code& ec) { - if (ec == asio::error::operation_aborted) return; - - mutex_t::scoped_lock l(m_mutex); - + CHECK_MAGIC; if (m_tunnel_packets) { // send udp packets through SOCKS5 server @@ -47,10 +60,26 @@ void udp_socket::send(udp::endpoint const& ep, char const* p, int len, error_cod void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_transferred) { - if (e == asio::error::operation_aborted) return; - + TORRENT_ASSERT(m_magic == 0x1337); mutex_t::scoped_lock l(m_mutex); + TORRENT_ASSERT(m_outstanding > 0); + --m_outstanding; + + if (e == asio::error::operation_aborted) + { + if (m_outstanding == 0) + { + // "this" may be destructed in the callback + // that's why we need to unlock + l.unlock(); + callback_t tmp = m_callback; + m_callback.clear(); + } + return; + } + + CHECK_MAGIC; if (!m_callback) return; if (e) @@ -59,9 +88,9 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_ try { #endif if (s == &m_ipv4_sock) - get_io_service().post(boost::bind(m_callback, e, m_v4_ep, (char*)0, 0)); + m_callback(e, m_v4_ep, 0, 0); else - get_io_service().post(boost::bind(m_callback, e, m_v6_ep, (char*)0, 0)); + m_callback(e, m_v6_ep, 0, 0); #ifndef BOOST_NO_EXCEPTIONS } catch(std::exception&) {} #endif @@ -94,7 +123,7 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_ if (m_tunnel_packets && m_v4_ep == m_proxy_addr) unwrap(e, m_v4_buf, bytes_transferred); else - get_io_service().post(boost::bind(m_callback, e, m_v4_ep, m_v4_buf, bytes_transferred)); + m_callback(e, m_v4_ep, m_v4_buf, bytes_transferred); #ifndef BOOST_NO_EXCEPTIONS } catch(std::exception&) {} @@ -111,7 +140,7 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_ if (m_tunnel_packets && m_v6_ep == m_proxy_addr) unwrap(e, m_v6_buf, bytes_transferred); else - get_io_service().post(boost::bind(m_callback, e, m_v6_ep, m_v6_buf, bytes_transferred)); + m_callback(e, m_v6_ep, m_v6_buf, bytes_transferred); #ifndef BOOST_NO_EXCEPTIONS } catch(std::exception&) {} @@ -119,10 +148,12 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_ s->async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf)) , m_v6_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2)); } + ++m_outstanding; } void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, error_code& ec) { + CHECK_MAGIC; using namespace libtorrent::detail; char header[20]; @@ -147,6 +178,7 @@ void udp_socket::wrap(udp::endpoint const& ep, char const* p, int len, error_cod // unwrap the UDP packet from the SOCKS5 header void udp_socket::unwrap(error_code const& e, char const* buf, int size) { + CHECK_MAGIC; using namespace libtorrent::detail; // the minimum socks5 header size @@ -177,27 +209,37 @@ void udp_socket::unwrap(error_code const& e, char const* buf, int size) return; } - get_io_service().post(boost::bind(m_callback, e, sender, p, size - (p - buf))); + m_callback(e, sender, p, size - (p - buf)); } void udp_socket::close() { + TORRENT_ASSERT(m_magic == 0x1337); mutex_t::scoped_lock l(m_mutex); error_code ec; m_ipv4_sock.close(ec); m_ipv6_sock.close(ec); m_socks5_sock.close(ec); - m_callback.clear(); if (m_connection_ticket >= 0) { m_cc.done(m_connection_ticket); m_connection_ticket = -1; } + + if (m_outstanding == 0) + { + // "this" may be destructed in the callback + // that's why we need to unlock + l.unlock(); + callback_t tmp = m_callback; + m_callback.clear(); + } } void udp_socket::bind(udp::endpoint const& ep, error_code& ec) { + CHECK_MAGIC; mutex_t::scoped_lock l(m_mutex); if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec); @@ -221,11 +263,13 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec) m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf)) , m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2)); } + ++m_outstanding; m_bind_port = ep.port(); } void udp_socket::bind(int port) { + CHECK_MAGIC; mutex_t::scoped_lock l(m_mutex); error_code ec; @@ -239,6 +283,7 @@ void udp_socket::bind(int port) m_ipv4_sock.bind(udp::endpoint(address_v4::any(), port), ec); m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf)) , m_v4_ep, boost::bind(&udp_socket::on_read, this, &m_ipv4_sock, _1, _2)); + ++m_outstanding; } m_ipv6_sock.open(udp::v6(), ec); if (!ec) @@ -247,12 +292,14 @@ void udp_socket::bind(int port) m_ipv6_sock.bind(udp::endpoint(address_v6::any(), port), ec); m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf)) , m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2)); + ++m_outstanding; } m_bind_port = port; } void udp_socket::set_proxy_settings(proxy_settings const& ps) { + CHECK_MAGIC; mutex_t::scoped_lock l(m_mutex); error_code ec; @@ -275,6 +322,7 @@ void udp_socket::set_proxy_settings(proxy_settings const& ps) void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i) { if (e) return; + CHECK_MAGIC; mutex_t::scoped_lock l(m_mutex); @@ -286,6 +334,7 @@ void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i) void udp_socket::on_timeout() { + CHECK_MAGIC; mutex_t::scoped_lock l(m_mutex); error_code ec; @@ -295,6 +344,7 @@ void udp_socket::on_timeout() void udp_socket::on_connect(int ticket) { + CHECK_MAGIC; mutex_t::scoped_lock l(m_mutex); m_connection_ticket = ticket; @@ -306,12 +356,13 @@ void udp_socket::on_connect(int ticket) void udp_socket::on_connected(error_code const& e) { + CHECK_MAGIC; + + mutex_t::scoped_lock l(m_mutex); m_cc.done(m_connection_ticket); m_connection_ticket = -1; if (e) return; - mutex_t::scoped_lock l(m_mutex); - using namespace libtorrent::detail; // send SOCKS5 authentication methods @@ -335,6 +386,7 @@ void udp_socket::on_connected(error_code const& e) void udp_socket::handshake1(error_code const& e) { + CHECK_MAGIC; if (e) return; mutex_t::scoped_lock l(m_mutex); @@ -345,6 +397,7 @@ void udp_socket::handshake1(error_code const& e) void udp_socket::handshake2(error_code const& e) { + CHECK_MAGIC; if (e) return; using namespace libtorrent::detail; @@ -390,6 +443,7 @@ void udp_socket::handshake2(error_code const& e) void udp_socket::handshake3(error_code const& e) { + CHECK_MAGIC; if (e) return; mutex_t::scoped_lock l(m_mutex); @@ -400,6 +454,7 @@ void udp_socket::handshake3(error_code const& e) void udp_socket::handshake4(error_code const& e) { + CHECK_MAGIC; if (e) return; mutex_t::scoped_lock l(m_mutex); @@ -418,6 +473,7 @@ void udp_socket::handshake4(error_code const& e) void udp_socket::socks_forward_udp() { + CHECK_MAGIC; using namespace libtorrent::detail; mutex_t::scoped_lock l(m_mutex); @@ -437,6 +493,7 @@ void udp_socket::socks_forward_udp() void udp_socket::connect1(error_code const& e) { + CHECK_MAGIC; if (e) return; mutex_t::scoped_lock l(m_mutex); @@ -447,6 +504,7 @@ void udp_socket::connect1(error_code const& e) void udp_socket::connect2(error_code const& e) { + CHECK_MAGIC; if (e) return; mutex_t::scoped_lock l(m_mutex);