Fix an object lifetime bug in net load tests
The commands handler must not be destroyed before the config object, or we'll be accessing freed memory. An earlier attempt at using boost::shared_ptr to control object lifetime turned out to be very invasive, though would be a better solution in theory.
This commit is contained in:
parent
86e9de588c
commit
7dbf76d0da
|
@ -87,6 +87,7 @@ namespace levin
|
|||
virtual void on_connection_new(t_connection_context& context){};
|
||||
virtual void on_connection_close(t_connection_context& context){};
|
||||
|
||||
virtual ~levin_commands_handler(){}
|
||||
};
|
||||
|
||||
#define LEVIN_OK 0
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace levin
|
|||
class levin_client_async
|
||||
{
|
||||
levin_commands_handler* m_pcommands_handler;
|
||||
void (*commands_handler_destroy)(levin_commands_handler*);
|
||||
volatile uint32_t m_is_stop;
|
||||
volatile uint32_t m_threads_count;
|
||||
::critical_section m_send_lock;
|
||||
|
@ -85,9 +86,9 @@ namespace levin
|
|||
::critical_section m_connection_lock;
|
||||
net_utils::blocked_mode_client m_transport;
|
||||
public:
|
||||
levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
|
||||
levin_client_async():m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
|
||||
{}
|
||||
levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
|
||||
levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
|
||||
{}
|
||||
~levin_client_async()
|
||||
{
|
||||
|
@ -97,11 +98,16 @@ namespace levin
|
|||
|
||||
while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count))
|
||||
::Sleep(100);
|
||||
|
||||
set_handler(NULL);
|
||||
}
|
||||
|
||||
void set_handler(levin_commands_handler* phandler)
|
||||
void set_handler(levin_commands_handler* phandler, void (*destroy)(levin_commands_handler*) = NULL)
|
||||
{
|
||||
if (commands_handler_destroy && m_pcommands_handler)
|
||||
(*commands_handler_destroy)(m_pcommands_handler);
|
||||
m_pcommands_handler = phandler;
|
||||
m_pcommands_handler_destroy = destroy;
|
||||
}
|
||||
|
||||
bool connect(uint32_t ip, uint32_t port, uint32_t timeout)
|
||||
|
|
|
@ -43,6 +43,8 @@ namespace levin
|
|||
struct protocl_handler_config
|
||||
{
|
||||
levin_commands_handler<t_connection_context>* m_pcommands_handler;
|
||||
void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*);
|
||||
~protocl_handler_config() { if (m_pcommands_handler && m_pcommands_handler_destroy) (*m_pcommands_handler_destroy)(m_pcommands_handler); }
|
||||
};
|
||||
|
||||
template<class t_connection_context = net_utils::connection_context_base>
|
||||
|
|
|
@ -72,9 +72,11 @@ class async_protocol_handler_config
|
|||
|
||||
friend class async_protocol_handler<t_connection_context>;
|
||||
|
||||
levin_commands_handler<t_connection_context>* m_pcommands_handler;
|
||||
void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*);
|
||||
|
||||
public:
|
||||
typedef t_connection_context connection_context;
|
||||
levin_commands_handler<t_connection_context>* m_pcommands_handler;
|
||||
uint64_t m_max_packet_size;
|
||||
uint64_t m_invoke_timeout;
|
||||
|
||||
|
@ -91,8 +93,9 @@ public:
|
|||
template<class callback_t>
|
||||
bool for_connection(const boost::uuids::uuid &connection_id, callback_t cb);
|
||||
size_t get_connections_count();
|
||||
void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL);
|
||||
|
||||
async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
|
||||
async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
|
||||
{}
|
||||
void del_out_connections(size_t count);
|
||||
};
|
||||
|
@ -832,6 +835,15 @@ size_t async_protocol_handler_config<t_connection_context>::get_connections_coun
|
|||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
void async_protocol_handler_config<t_connection_context>::set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*))
|
||||
{
|
||||
if (m_pcommands_handler && m_pcommands_handler_destroy)
|
||||
(*m_pcommands_handler_destroy)(m_pcommands_handler);
|
||||
m_pcommands_handler = handler;
|
||||
m_pcommands_handler_destroy = destroy;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id)
|
||||
{
|
||||
async_protocol_handler<t_connection_context>* aph;
|
||||
|
|
|
@ -155,7 +155,7 @@ namespace tests
|
|||
|
||||
bool init(const std::string& bind_port = "", const std::string& bind_ip = "0.0.0.0")
|
||||
{
|
||||
m_net_server.get_config_object().m_pcommands_handler = this;
|
||||
m_net_server.get_config_object().set_handler(this);
|
||||
m_net_server.get_config_object().m_invoke_timeout = 1000;
|
||||
LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port);
|
||||
return m_net_server.init_server(bind_port, bind_ip);
|
||||
|
|
|
@ -560,7 +560,7 @@ namespace nodetool
|
|||
|
||||
//configure self
|
||||
m_net_server.set_threads_prefix("P2P");
|
||||
m_net_server.get_config_object().m_pcommands_handler = this;
|
||||
m_net_server.get_config_object().set_handler(this);
|
||||
m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT;
|
||||
m_net_server.set_connection_filter(this);
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ namespace
|
|||
{
|
||||
m_thread_count = (std::max)(min_thread_count, boost::thread::hardware_concurrency() / 2);
|
||||
|
||||
m_tcp_server.get_config_object().m_pcommands_handler = &m_commands_handler;
|
||||
m_tcp_server.get_config_object().set_handler(&m_commands_handler);
|
||||
m_tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT;
|
||||
|
||||
ASSERT_TRUE(m_tcp_server.init_server(clt_port, "127.0.0.1"));
|
||||
|
@ -238,9 +238,10 @@ namespace
|
|||
static void TearDownTestCase()
|
||||
{
|
||||
// Stop server
|
||||
test_levin_commands_handler commands_handler;
|
||||
test_tcp_server tcp_server(epee::net_utils::e_connection_type_NET);
|
||||
tcp_server.get_config_object().m_pcommands_handler = &commands_handler;
|
||||
test_levin_commands_handler *commands_handler_ptr = new test_levin_commands_handler();
|
||||
test_levin_commands_handler &commands_handler = *commands_handler_ptr;
|
||||
test_tcp_server tcp_server(epee::net_utils::e_connection_type_RPC);
|
||||
tcp_server.get_config_object().set_handler(commands_handler_ptr, [](epee::levin::levin_commands_handler<test_connection_context> *handler)->void { delete handler; });
|
||||
tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT;
|
||||
|
||||
if (!tcp_server.init_server(clt_port, "127.0.0.1")) return;
|
||||
|
|
|
@ -151,6 +151,11 @@ namespace net_load_tests
|
|||
bool handle_new_connection(const boost::uuids::uuid& connection_id, bool ignore_close_fails = false)
|
||||
{
|
||||
size_t idx = m_next_opened_conn_idx.fetch_add(1, std::memory_order_relaxed);
|
||||
if (idx >= m_connections.size())
|
||||
{
|
||||
LOG_PRINT_L0("ERROR: connections overflow");
|
||||
exit(1);
|
||||
}
|
||||
m_connections[idx] = connection_id;
|
||||
|
||||
size_t prev_connection_count = m_opened_connection_count.fetch_add(1, std::memory_order_relaxed);
|
||||
|
|
|
@ -224,8 +224,8 @@ int main(int argc, char** argv)
|
|||
if (!tcp_server.init_server(srv_port, "127.0.0.1"))
|
||||
return 1;
|
||||
|
||||
srv_levin_commands_handler commands_handler(tcp_server);
|
||||
tcp_server.get_config_object().m_pcommands_handler = &commands_handler;
|
||||
srv_levin_commands_handler *commands_handler = new srv_levin_commands_handler(tcp_server);
|
||||
tcp_server.get_config_object().set_handler(commands_handler, [](epee::levin::levin_commands_handler<test_connection_context> *handler) { delete handler; });
|
||||
tcp_server.get_config_object().m_invoke_timeout = 10000;
|
||||
//tcp_server.get_config_object().m_max_packet_size = max_packet_size;
|
||||
|
||||
|
|
|
@ -187,9 +187,11 @@ namespace
|
|||
|
||||
typedef std::unique_ptr<test_connection> test_connection_ptr;
|
||||
|
||||
async_protocol_handler_test()
|
||||
async_protocol_handler_test():
|
||||
m_pcommands_handler(new test_levin_commands_handler()),
|
||||
m_commands_handler(*m_pcommands_handler)
|
||||
{
|
||||
m_handler_config.m_pcommands_handler = &m_commands_handler;
|
||||
m_handler_config.set_handler(m_pcommands_handler, [](epee::levin::levin_commands_handler<test_levin_connection_context> *handler) { delete handler; });
|
||||
m_handler_config.m_invoke_timeout = invoke_timeout;
|
||||
m_handler_config.m_max_packet_size = max_packet_size;
|
||||
}
|
||||
|
@ -212,7 +214,7 @@ namespace
|
|||
protected:
|
||||
boost::asio::io_service m_io_service;
|
||||
test_levin_protocol_handler_config m_handler_config;
|
||||
test_levin_commands_handler m_commands_handler;
|
||||
test_levin_commands_handler *m_pcommands_handler, &m_commands_handler;
|
||||
};
|
||||
|
||||
class positive_test_connection_to_levin_protocol_handler_calls : public async_protocol_handler_test
|
||||
|
|
Loading…
Reference in New Issue