Bug #98980 A state inside mysql_real_connect_nonblocking still blocks
Submitted: 17 Mar 2020 19:30 Modified: 20 May 2020 17:55
Reporter: Jay Edgar Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S3 (Non-critical)
Version:8.0.19 OS:Any
Assigned to: CPU Architecture:Any

[17 Mar 2020 19:30] Jay Edgar
Description:
After fixing the top-level nonblocking issue for mysql_real_connect_nonblocking() (https://bugs.mysql.com/bug.php?id=98574) it appears that the csm_complete_connect state inside it is still blocking.  The function always goes into a wait state if there is a connect timeout specified.

How to repeat:
After applying the fix for 98574, make the following changes:

diff --git a/testclients/mysql_client_test.cc b/testclients/mysql_client_test.cc
index 2c45d41da89..31b33cbd025 100644
--- a/testclients/mysql_client_test.cc
+++ b/testclients/mysql_client_test.cc
@@ -30,6 +30,8 @@
   contains only the actual tests, plus the list of test functions to call.
 */

+#include <chrono>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -20580,6 +20592,42 @@ static void test_bug30032302() {
   check_warning(mysql);
 }

+static void test_csm_complete_connect() {
+  MYSQL *mysql_local;
+
+  // Create a socket for the client to connect to.  If nonblocking is working
+  // correctly the mysql_real_connect_nonblocking will always return
+  // NET_ASYNC_NOT_READY almost immediately.
+  if (!(mysql_local = mysql_client_init(NULL))) {
+    myerror("mysql_client_init() failed");
+    exit(1);
+  }
+
+  uint ctimeout = 5;
+  mysql_options(mysql_local, MYSQL_OPT_CONNECT_TIMEOUT, &ctimeout);
+
+  auto port = 30000;
+  auto sock = socket(AF_INET, SOCK_STREAM, 0);
+  sockaddr_in addr;
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(port);
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  auto res  = bind(sock, (sockaddr*) &addr, sizeof(addr));
+  DIE_UNLESS(res  != -1);
+  res  = listen(sock, 1);
+  DIE_UNLESS(res  != -1);
+  for (auto ii = 0; ii < 5; ii++) {
+    auto start = std::chrono::steady_clock::now();
+    auto status = mysql_real_connect_nonblocking(
+        mysql_local, "127.0.0.1", opt_user, opt_password, current_db, port,
+        opt_unix_socket, CLIENT_MULTI_STATEMENTS);
+    auto end = std::chrono::steady_clock::now();
+    auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(end - start);
+    DIE_UNLESS(elapsed.count() < 1);
+    DIE_UNLESS(status == NET_ASYNC_NOT_READY);
+  }
+}
+
 static struct my_tests_st my_tests[] = {
     {"disable_query_logs", disable_query_logs},
     {"test_view_sp_list_fields", test_view_sp_list_fields},
@@ -20866,6 +20914,7 @@ static struct my_tests_st my_tests[] = {
     {"test_wl11772", test_wl11772},
     {"test_wl12475", test_wl12475},
     {"test_bug30032302", test_bug30032302},
+    {"test_csm_complete_connect", test_csm_complete_connect},
     {0, 0}};

 static struct my_tests_st *get_my_tests() { return my_tests; }

Then when running the mysql_client_test the system will fail because the mysql_real_connect_nonblocking will block for 5 seconds.  With the following fix the test will run correctly.

Suggested fix:
diff --git a/sql-common/client.cc b/sql-common/client.cc
index fd36e9950cf..8d66cbdbb0c 100644
--- a/sql-common/client.cc
+++ b/sql-common/client.cc
@@ -6174,6 +6179,7 @@ static mysql_state_machine_status csm_complete_connect(
   /* Get version info */
   mysql->protocol_version = PROTOCOL_VERSION; /* Assume this */
   if (mysql->options.connect_timeout &&
+      !ctx->non_blocking &&
       (vio_io_wait(net->vio, VIO_IO_EVENT_READ,
                    get_vio_connect_timeout(mysql)) < 1)) {
     set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
[18 Mar 2020 14:52] MySQL Verification Team
Hi Mr. Edgar,

Thank you for your bug report.

I have repeated your test case and got the same result as reported.

Verified as reported.
[20 May 2020 17:55] Paul DuBois
Posted by developer:
 
Fixed in 8.0.22.

mysql_real_connect_nonblocking() blocked if the
MYSQL_OPT_CONNECT_TIMEOUT option was set.
[21 May 2020 11:57] MySQL Verification Team
Thank you, Paul.