8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 22:03:03 +01:00

Backport fix for bug CORE-3119 : 100% CPU USAGE with Unilimited Loop & Index corrupted

Backport related fixes from v2.5 :
Added timeouts to the network event listener code.
Fixed wrong usage of SO_REUSEADDR on Windows.
This commit is contained in:
hvlad 2010-12-12 19:22:36 +00:00
parent 7505dbd9ad
commit 90b88fdec3
2 changed files with 97 additions and 17 deletions

View File

@ -264,7 +264,7 @@ static int accept_connection(rem_port*, P_CNCT *);
#ifdef HAVE_SETITIMER #ifdef HAVE_SETITIMER
static void alarm_handler(int); static void alarm_handler(int);
#endif #endif
static rem_port* alloc_port(rem_port*); static rem_port* alloc_port(rem_port*, const USHORT = 0);
static rem_port* aux_connect(rem_port*, PACKET*, t_event_ast); static rem_port* aux_connect(rem_port*, PACKET*, t_event_ast);
static rem_port* aux_request(rem_port*, PACKET*); static rem_port* aux_request(rem_port*, PACKET*);
#if !defined(WIN_NT) #if !defined(WIN_NT)
@ -867,6 +867,12 @@ rem_port* INET_connect(const TEXT* name,
lingerInfo.l_onoff = 0; lingerInfo.l_onoff = 0;
lingerInfo.l_linger = 0; lingerInfo.l_linger = 0;
#ifndef WIN_NT
// dimitr: on Windows, lack of SO_REUSEADDR works the same way as it was specified on POSIX,
// i.e. it allows binding to a port in a TIME_WAIT/FIN_WAIT state. If this option
// is turned on explicitly, then a port can be re-bound regardless of its state,
// e.g. while it's listening. This is surely not what we want.
int optval = TRUE; int optval = TRUE;
n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_REUSEADDR, n = setsockopt((SOCKET) port->port_handle, SOL_SOCKET, SO_REUSEADDR,
(SCHAR*) &optval, sizeof(optval)); (SCHAR*) &optval, sizeof(optval));
@ -876,6 +882,7 @@ rem_port* INET_connect(const TEXT* name,
disconnect(port); disconnect(port);
return NULL; return NULL;
} }
#endif
/* Get any values for SO_LINGER so that they can be reset during /* Get any values for SO_LINGER so that they can be reset during
* disconnect. SO_LINGER should be set by default on the socket * disconnect. SO_LINGER should be set by default on the socket
@ -916,7 +923,7 @@ rem_port* INET_connect(const TEXT* name,
sleep(10); sleep(10);
n = bind((SOCKET) port->port_handle, n = bind((SOCKET) port->port_handle,
(struct sockaddr *) &address, sizeof(address)); (struct sockaddr *) &address, sizeof(address));
if (n == 0) if (n == 0 || INET_ERRNO != INET_ADDR_IN_USE)
break; break;
} }
} }
@ -1304,7 +1311,7 @@ static int accept_connection(rem_port* port,
} }
static rem_port* alloc_port( rem_port* parent) static rem_port* alloc_port( rem_port* parent, const USHORT flags)
{ {
/************************************** /**************************************
* *
@ -1353,6 +1360,9 @@ static rem_port* alloc_port( rem_port* parent)
rem_port* port = (rem_port*) ALLR_block(type_port, INET_remote_buffer * 2); rem_port* port = (rem_port*) ALLR_block(type_port, INET_remote_buffer * 2);
port->port_type = port_inet; port->port_type = port_inet;
port->port_state = state_pending; port->port_state = state_pending;
port->port_flags = flags;
port->port_handle = (HANDLE) INVALID_SOCKET;
port->port_channel = INVALID_SOCKET;
REMOTE_get_timeout_params(port, 0, 0); REMOTE_get_timeout_params(port, 0, 0);
gethostname(buffer, sizeof(buffer)); gethostname(buffer, sizeof(buffer));
@ -1366,7 +1376,6 @@ static rem_port* alloc_port( rem_port* parent)
port->port_parent = parent; port->port_parent = parent;
port->port_next = parent->port_clients; port->port_next = parent->port_clients;
parent->port_clients = parent->port_next = port; parent->port_clients = parent->port_next = port;
port->port_handle = parent->port_handle;
port->port_server = parent->port_server; port->port_server = parent->port_server;
port->port_server_flags = parent->port_server_flags; port->port_server_flags = parent->port_server_flags;
} }
@ -1422,9 +1431,51 @@ static rem_port* aux_connect(rem_port* port, PACKET* packet, t_event_ast ast)
if (port->port_server_flags) { if (port->port_server_flags) {
struct timeval timeout;
timeout.tv_sec = port->port_connect_timeout;
timeout.tv_usec = 0;
fd_set slct_fdset;
FD_ZERO(&slct_fdset);
FD_SET(port->port_channel, &slct_fdset);
int inetErrNo = 0;
while (true)
{
THREAD_EXIT();
const int count =
#ifdef WIN_NT
select(FD_SETSIZE, &slct_fdset, NULL, NULL, &timeout);
#else
select(port->port_channel + 1, &slct_fdset, NULL, NULL, &timeout);
#endif
inetErrNo = INET_ERRNO;
THREAD_ENTER();
if (count != -1 || !INTERRUPT_ERROR(inetErrNo))
{
if (count == 1)
{
break;
}
else
{
if (count == 0)
inet_gen_error(port,
isc_random, isc_arg_string, "Timeout occurred while waiting for a secondary connection for event processing",
isc_arg_end);
else
inet_error(port, "select", isc_net_event_connect_err, inetErrNo);
SOCLOSE(port->port_channel);
return NULL;
}
}
}
THREAD_EXIT(); THREAD_EXIT();
SOCKET n = accept(port->port_channel, (struct sockaddr *) &address, &l); SOCKET n = accept(port->port_channel, (struct sockaddr *) &address, &l);
const int inetErrNo = INET_ERRNO; inetErrNo = INET_ERRNO;
THREAD_ENTER(); THREAD_ENTER();
if (n == INVALID_SOCKET) { if (n == INVALID_SOCKET) {
@ -1542,6 +1593,12 @@ static rem_port* aux_request( rem_port* port, PACKET* packet)
return NULL; return NULL;
} }
#ifndef WIN_NT
// dimitr: on Windows, lack of SO_REUSEADDR works the same way as it was specified on POSIX,
// i.e. it allows binding to a port in a TIME_WAIT/FIN_WAIT state. If this option
// is turned on explicitly, then a port can be re-bound regardless of its state,
// e.g. while it's listening. This is surely not what we want.
int optval = TRUE; int optval = TRUE;
int ret = setsockopt(n, SOL_SOCKET, SO_REUSEADDR, int ret = setsockopt(n, SOL_SOCKET, SO_REUSEADDR,
(SCHAR *) &optval, sizeof(optval)); (SCHAR *) &optval, sizeof(optval));
@ -1550,6 +1607,7 @@ static rem_port* aux_request( rem_port* port, PACKET* packet)
inet_error(port, "setsockopt REUSE", isc_net_event_listen_err, INET_ERRNO); inet_error(port, "setsockopt REUSE", isc_net_event_listen_err, INET_ERRNO);
return NULL; return NULL;
} }
#endif
if (bind(n, (struct sockaddr *) &address, sizeof(address)) < 0) { if (bind(n, (struct sockaddr *) &address, sizeof(address)) < 0) {
inet_error(port, "bind", isc_net_event_listen_err, INET_ERRNO); inet_error(port, "bind", isc_net_event_listen_err, INET_ERRNO);
@ -1568,14 +1626,14 @@ static rem_port* aux_request( rem_port* port, PACKET* packet)
return NULL; return NULL;
} }
rem_port* new_port = alloc_port(port->port_parent); rem_port* new_port = alloc_port(port->port_parent, PORT_async);
port->port_async = new_port; port->port_async = new_port;
new_port->port_dummy_packet_interval = port->port_dummy_packet_interval; new_port->port_dummy_packet_interval = port->port_dummy_packet_interval;
new_port->port_dummy_timeout = new_port->port_dummy_packet_interval; new_port->port_dummy_timeout = new_port->port_dummy_packet_interval;
new_port->port_server_flags = port->port_server_flags; new_port->port_server_flags = port->port_server_flags;
new_port->port_channel = (int) n; new_port->port_channel = (int) n;
new_port->port_flags = port->port_flags & PORT_no_oob; new_port->port_flags |= port->port_flags & PORT_no_oob;
P_RESP* response = &packet->p_resp; P_RESP* response = &packet->p_resp;
@ -2619,6 +2677,9 @@ static rem_port* select_port( rem_port* main_port, SLCT * selct)
for (rem_port* port = main_port; port; port = port->port_next) { for (rem_port* port = main_port; port; port = port->port_next) {
const int n = (int) port->port_handle; const int n = (int) port->port_handle;
if (n < 0 || n >= FD_SETSIZE) { if (n < 0 || n >= FD_SETSIZE) {
if (port->port_flags & PORT_disconnect) {
continue;
}
STOP_PORT_CRITICAL(); STOP_PORT_CRITICAL();
return port; return port;
} }
@ -2684,8 +2745,9 @@ static int select_wait( rem_port* main_port, SLCT * selct)
#endif #endif
for (rem_port* port = main_port; port; port = port->port_next) for (rem_port* port = main_port; port; port = port->port_next)
{ {
if ((port->port_state == state_active) || if (((port->port_state == state_active) || (port->port_state == state_pending)) &&
(port->port_state == state_pending)) // don't wait on still listening (not connected) async port
!((SOCKET)port->port_handle == INVALID_SOCKET && port->port_flags & PORT_async) )
{ {
/* Adjust down the port's keepalive timer. */ /* Adjust down the port's keepalive timer. */
@ -2742,7 +2804,7 @@ static int select_wait( rem_port* main_port, SLCT * selct)
++selct->slct_width; ++selct->slct_width;
#else #else
selct->slct_width = selct->slct_width =
MAX(selct->slct_width, (int) port->port_handle); MAX(selct->slct_width, (int) port->port_handle + 1);
#endif #endif
found = true; found = true;
} }
@ -2757,7 +2819,6 @@ static int select_wait( rem_port* main_port, SLCT * selct)
} }
THREAD_EXIT(); THREAD_EXIT();
++selct->slct_width;
for (;;) for (;;)
{ {
@ -2776,6 +2837,7 @@ static int select_wait( rem_port* main_port, SLCT * selct)
const int inetErrNo = INET_ERRNO; const int inetErrNo = INET_ERRNO;
if (selct->slct_count != -1) if (selct->slct_count != -1)
{ {
THREAD_ENTER();
/* if selct->slct_count is zero it means that we timed out of /* if selct->slct_count is zero it means that we timed out of
select with nothing to read or accept, so clear the fd_set select with nothing to read or accept, so clear the fd_set
bit as this value is undefined on some platforms (eg. HP-UX), bit as this value is undefined on some platforms (eg. HP-UX),
@ -2783,16 +2845,20 @@ static int select_wait( rem_port* main_port, SLCT * selct)
they can be used in select_port() */ they can be used in select_port() */
if (selct->slct_count == 0) if (selct->slct_count == 0)
{ {
START_PORT_CRITICAL();
for (rem_port* port = main_port; port; port = port->port_next) for (rem_port* port = main_port; port; port = port->port_next)
{ {
if (!( (SOCKET)port->port_handle == INVALID_SOCKET && port->port_flags & PORT_async))
{
#ifdef WIN_NT #ifdef WIN_NT
FD_CLR((SOCKET)port->port_handle, &selct->slct_fdset); FD_CLR((SOCKET)port->port_handle, &selct->slct_fdset);
#else #else
FD_CLR(port->port_handle, &selct->slct_fdset); FD_CLR(port->port_handle, &selct->slct_fdset);
#endif #endif
}
} }
STOP_PORT_CRITICAL();
} }
THREAD_ENTER();
return TRUE; return TRUE;
} }
else if (INTERRUPT_ERROR(inetErrNo)) else if (INTERRUPT_ERROR(inetErrNo))

View File

@ -1155,9 +1155,23 @@ static void aux_request( rem_port* port, P_REQ * request, PACKET* send)
return; return;
} }
if (aux_port) { if (aux_port)
aux_port->connect(send, 0); {
aux_port->port_context = rdb; ISC_STATUS* const save_status = aux_port->port_status_vector;
aux_port->port_status_vector = status_vector;
if (aux_port->connect(send, 0))
{
aux_port->port_context = rdb;
aux_port->port_status_vector = save_status;
}
else
{
gds__log_status(NULL, aux_port->port_status_vector);
fb_assert(port->port_async == aux_port);
port->port_async = NULL;
aux_port->disconnect();
}
} }
/* restore the port status vector */ /* restore the port status vector */