diff options
author | Davi Arnaut <davi.arnaut@oracle.com> | 2010-12-15 20:59:21 -0200 |
---|---|---|
committer | Davi Arnaut <davi.arnaut@oracle.com> | 2010-12-15 20:59:21 -0200 |
commit | 4e2cf44180cac803013d086bd50fe06ab3d863a5 (patch) | |
tree | 40ac241a8b0dc4aaa69489f0e28b7b759da0511f /sql/mysqld.cc | |
parent | 4ccb32c09aa0a4c9678e8cf499a552259c19d743 (diff) | |
download | mariadb-git-4e2cf44180cac803013d086bd50fe06ab3d863a5.tar.gz |
Bug#58136: Crash in vio_close at concurrent disconnect and KILL
The problem is a race between a session closing its vio
(i.e. after a COM_QUIT) at the same time it is being killed by
another thread. This could trigger a assertion in vio_close()
as the two threads could end up closing the same vio, at the
same time. This could happen due to the implementation of
SIGNAL_WITH_VIO_CLOSE, which closes the vio of the thread
being killed.
The solution is to serialize the close of the Vio under
LOCK_thd_data, which protects THD data.
No regression test is added as this is essentially a debug
issue and the test case would be quite convoluted as we would
need to synchronize a session that is being killed -- which
is a bit difficult since debug sync points code does not
synchronize killed sessions.
sql/mysqld.cc:
Drop lock parameter from close_connection, its not necessary
any more. The newly introduced THD::disconnect method will take
care of locking.
sql/mysqld.h:
Change prototype, add a default parameter for the error code.
sql/sql_class.cc:
In case SIGNAL_WITH_VIO_CLOSE is defined, the active vio is
closed and cleared. Subsequent calls will only close the vio
owned by the session.
Diffstat (limited to 'sql/mysqld.cc')
-rw-r--r-- | sql/mysqld.cc | 45 |
1 files changed, 17 insertions, 28 deletions
diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5fb63fb61ba..68795ca377b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1134,7 +1134,7 @@ static void close_connections(void) tmp->thread_id, (tmp->main_security_ctx.user ? tmp->main_security_ctx.user : "")); - close_connection(tmp,0,0); + close_connection(tmp); } #endif DBUG_PRINT("quit",("Unlocking LOCK_thread_count")); @@ -1960,39 +1960,28 @@ static void network_init(void) /** Close a connection. - @param thd Thread handle - @param errcode Error code to print to console - @param lock 1 if we have have to lock LOCK_thread_count + @param thd Thread handle. + @param sql_errno The error code to send before disconnect. @note For the connection that is doing shutdown, this is called twice */ -void close_connection(THD *thd, uint errcode, bool lock) +void close_connection(THD *thd, uint sql_errno) { - st_vio *vio; DBUG_ENTER("close_connection"); - DBUG_PRINT("enter",("fd: %s error: '%s'", - thd->net.vio ? vio_description(thd->net.vio) : - "(not connected)", - errcode ? ER_DEFAULT(errcode) : "")); - if (lock) - mysql_mutex_lock(&LOCK_thread_count); - thd->killed= THD::KILL_CONNECTION; - if ((vio= thd->net.vio) != 0) - { - if (errcode) - net_send_error(thd, errcode, - ER_DEFAULT(errcode), NULL); /* purecov: inspected */ - vio_close(vio); /* vio is freed in delete thd */ - } - if (lock) - mysql_mutex_unlock(&LOCK_thread_count); - MYSQL_CONNECTION_DONE((int) errcode, thd->thread_id); + + if (sql_errno) + net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL); + + thd->disconnect(); + + MYSQL_CONNECTION_DONE((int) sql_errno, thd->thread_id); + if (MYSQL_CONNECTION_DONE_ENABLED()) { sleep(0); /* Workaround to avoid tailcall optimisation */ } - MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, errcode); + MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, sql_errno); DBUG_VOID_RETURN; } #endif /* EMBEDDED_LIBRARY */ @@ -4951,8 +4940,8 @@ void create_thread_to_handle_connection(THD *thd) my_snprintf(error_message_buff, sizeof(error_message_buff), ER_THD(thd, ER_CANT_CREATE_THREAD), error); net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL); + close_connection(thd); mysql_mutex_lock(&LOCK_thread_count); - close_connection(thd,0,0); delete thd; mysql_mutex_unlock(&LOCK_thread_count); return; @@ -4993,7 +4982,7 @@ static void create_new_thread(THD *thd) mysql_mutex_unlock(&LOCK_connection_count); DBUG_PRINT("error",("Too many connections")); - close_connection(thd, ER_CON_COUNT_ERROR, 1); + close_connection(thd, ER_CON_COUNT_ERROR); delete thd; DBUG_VOID_RETURN; } @@ -5374,7 +5363,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg) if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) || my_net_init(&thd->net, thd->net.vio)) { - close_connection(thd, ER_OUT_OF_RESOURCES, 1); + close_connection(thd, ER_OUT_OF_RESOURCES); delete thd; continue; } @@ -5569,7 +5558,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg) event_conn_closed)) || my_net_init(&thd->net, thd->net.vio)) { - close_connection(thd, ER_OUT_OF_RESOURCES, 1); + close_connection(thd, ER_OUT_OF_RESOURCES); errmsg= 0; goto errorconn; } |