diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 9d4b0670..4f70cf53 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -63,6 +63,9 @@ #ifdef HAVE_OPENSSL #include #endif +#ifndef _WIN32 +#include +#endif static my_bool mysql_client_init=0; static void mysql_close_options(MYSQL *mysql); @@ -145,93 +148,81 @@ extern void mysql_client_plugin_deinit(); * Base version coded by Steve Bernacki, Jr. *****************************************************************************/ +int socket_block(my_socket s,my_bool blocked) +{ +#ifdef _WIN32 + unsigned long socket_blocked= blocked ? 0 : 1; + return ioctlsocket(s, FIONBIO, &socket_blocked); +#else + int flags= fcntl(s, F_GETFL, 0); + if (blocked) + flags&= ~O_NONBLOCK; + else + flags|= O_NONBLOCK; + return fcntl(s, F_SETFL, flags); +#endif +} + static int connect2(my_socket s, const struct sockaddr *name, uint namelen, uint timeout) { -#if defined(_WIN32) || defined(OS2) - return connect(s, (struct sockaddr*) name, namelen); -#else - int flags, res, s_err; + int res, s_err; socklen_t s_err_size = sizeof(uint); - fd_set sfds; +#ifndef _WIN32 + struct pollfd poll_fd; +#else + FD_SET sfds, efds; struct timeval tv; - time_t start_time, now_time; +#endif - /* - If they passed us a timeout of zero, we should behave - exactly like the normal connect() call does. - */ - - if (timeout == 0) + if (!timeout) return connect(s, (struct sockaddr*) name, namelen); - flags = fcntl(s, F_GETFL, 0); /* Set socket to not block */ -#ifdef O_NONBLOCK - fcntl(s, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */ -#endif + /* set socket to non blocking */ + if (socket_block(s, 0) == SOCKET_ERROR) + return -1; - res = connect(s, (struct sockaddr*) name, namelen); - s_err = errno; /* Save the error... */ - fcntl(s, F_SETFL, flags); - if ((res != 0) && (s_err != EINPROGRESS)) - { - errno = s_err; /* Restore it */ - return(-1); - } - if (res == 0) /* Connected quickly! */ - return(0); + res= connect(s, (struct sockaddr*) name, namelen); + if (res == 0) + return res; - /* - Otherwise, our connection is "in progress." We can use - the select() call to wait up to a specified period of time - for the connection to suceed. If select() returns 0 - (after waiting howevermany seconds), our socket never became - writable (host is probably unreachable.) Otherwise, if - select() returns 1, then one of two conditions exist: - - 1. An error occured. We use getsockopt() to check for this. - 2. The connection was set up sucessfully: getsockopt() will - return 0 as an error. - - Thanks goes to Andrew Gierth - who posted this method of timing out a connect() in - comp.unix.programmer on August 15th, 1997. - */ - - FD_ZERO(&sfds); - FD_SET(s, &sfds); - /* - select could be interrupted by a signal, and if it is, - the timeout should be adjusted and the select restarted - to work around OSes that don't restart select and - implementations of select that don't adjust tv upon - failure to reflect the time remaining - */ - start_time = time(NULL); - for (;;) - { - tv.tv_sec = (long) timeout; - tv.tv_usec = 0; -#if defined(HPUX) && defined(THREAD) - if ((res = select(s+1, NULL, (int*) &sfds, NULL, &tv)) > 0) - break; +#ifdef _WIN32 + if (GetLastError() != WSAEWOULDBLOCK && + GetLastError() != WSAEINPROGRESS) #else - if ((res = select(s+1, NULL, &sfds, NULL, &tv)) > 0) - break; + if (errno != EINPROGRESS) #endif - if (res == 0) /* timeout */ - return -1; - now_time=time(NULL); - timeout-= (uint) (now_time - start_time); - if (errno != EINTR || (int) timeout <= 0) - return -1; - } + return -1; +#ifndef _WIN32 + memset(&poll_fd, 0, sizeof(struct pollfd)); + poll_fd.events= POLLOUT | POLLERR; + poll_fd.fd= s; - /* - select() returned something more interesting than zero, let's - see if we have any errors. If the next two statements pass, - we've got an open socket! - */ + /* connection timeout in milliseconds */ + res= poll(&poll_fd, 1, (timeout > -1) ? timeout * 1000 : timeout); + + switch(res) + { + /* Error= - 1, timeout = 0 */ + case -1: + break; + case 0: + errno= ETIMEDOUT; + break; + } +#else + FD_ZERO(&sfds); + FD_ZERO(&efds); + FD_SET(s, &sfds); + FD_SET(s, &efds); + + memset(&tv, 0, sizeof(struct timeval)); + tv.tv_sec= timeout; + + res= select(s+1, NULL, &sfds, &efds, &tv); + if (res < 1) + return -1; +#endif s_err=0; if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) @@ -243,8 +234,6 @@ static int connect2(my_socket s, const struct sockaddr *name, uint namelen, return(-1); /* but return an error... */ } return (0); /* ok */ - -#endif } /* @@ -1521,6 +1510,8 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, unix_socket, socket_errno); goto error; } + if (socket_block(sock, 1) == SOCKET_ERROR) + goto error; } else #elif defined(_WIN32) @@ -1610,7 +1601,14 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, } if (!(rc= connect2(sock, save_res->ai_addr, save_res->ai_addrlen, mysql->options.connect_timeout))) + { + if (socket_block(sock, 1) == SOCKET_ERROR) + { + closesocket(sock); + continue; + } break; /* success! */ + } vio_delete(mysql->net.vio); mysql->net.vio= NULL; diff --git a/libmariadb/my_pthread.c b/libmariadb/my_pthread.c index fd8dcd33..37d4e58e 100644 --- a/libmariadb/my_pthread.c +++ b/libmariadb/my_pthread.c @@ -33,6 +33,7 @@ pthread_cond_init (pthread_cond_t *cv, const pthread_condattr_t *attr) { DBUG_ENTER("pthread_cond_init"); /* Initialize the count to 0 */ + InitializeCriticalSection(&cv->waiters_count_lock); cv->waiting = 0; /* Create an auto-reset and manual-reset event */ diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index dc779f72..5ea23368 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -522,7 +522,10 @@ static int test_reconnect(MYSQL *mysql) diag("Thread_id before kill: %lu", mysql_thread_id(mysql1)); mysql_kill(mysql, mysql_thread_id(mysql1)); + sleep(2); + rc= mysql_query(mysql1, "SELECT 1 FROM DUAL LIMIT 0"); + FAIL_IF(rc == 0, "error expected"); rc= mysql_query(mysql1, "SELECT 1 FROM DUAL LIMIT 0"); check_mysql_rc(rc, mysql1); diag("Thread_id after kill: %lu", mysql_thread_id(mysql1)); @@ -581,6 +584,25 @@ int test_conc26(MYSQL *my) return OK; } +int test_connection_timeout(MYSQL *my) +{ + unsigned int timeout= 5; + time_t start, elapsed; + MYSQL *mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + start= time(NULL); + if (mysql_real_connect(mysql, "192.168.1.101", "notexistinguser", "password", schema, port, NULL, CLIENT_REMEMBER_OPTIONS)) + { + diag("Error expected - maybe you have to change hostname"); + return FAIL; + } + elapsed= time(NULL) - start; + diag("elapsed: %d", elapsed); + mysql_close(mysql); + FAIL_IF(elapsed +1 > timeout, "timeout ignored") + return OK; +} + struct my_tests_st my_tests[] = { {"test_bug20023", test_bug20023, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_bug31669", test_bug31669, TEST_CONNECTION_NEW, 0, NULL, NULL}, @@ -591,6 +613,7 @@ struct my_tests_st my_tests[] = { {"test_reconnect", test_reconnect, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc21", test_conc21, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc26", test_conc26, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_connection_timeout", test_connection_timeout, TEST_CONNECTION_NONE, 0, NULL, NULL}, {NULL, NULL, 0, 0, NULL, NULL} }; diff --git a/unittest/libmariadb/thread.c b/unittest/libmariadb/thread.c index 1b8db900..708a7692 100644 --- a/unittest/libmariadb/thread.c +++ b/unittest/libmariadb/thread.c @@ -45,8 +45,9 @@ int thread_conc27(void); DWORD WINAPI thread_conc27(void); #endif -#define THREAD_NUM 150 +#define THREAD_NUM 1800 +/* run this test as root and increase the number of handles (ulimit -n) */ static int test_conc_27(MYSQL *mysql) { @@ -70,6 +71,9 @@ static int test_conc_27(MYSQL *mysql) rc= mysql_query(mysql, "INSERT INTO t_conc27 VALUES(0)"); check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "SET GLOBAL max_connections=100000"); + check_mysql_rc(rc, mysql); + pthread_mutex_init(&LOCK_test, NULL); for (i=0; i < THREAD_NUM; i++) { @@ -121,7 +125,7 @@ DWORD WINAPI thread_conc27(void) if(!mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0)) { - diag("Error: %s", mysql_error(mysql)); + diag(">Error: %s", mysql_error(mysql)); mysql_close(mysql); mysql_thread_end(); goto end; @@ -130,7 +134,6 @@ DWORD WINAPI thread_conc27(void) rc= mysql_query(mysql, "UPDATE t_conc27 SET a=a+1"); check_mysql_rc(rc, mysql); pthread_mutex_unlock(&LOCK_test); - rc= mysql_query(mysql, "SELECT SLEEP(5)"); check_mysql_rc(rc, mysql); if (res= mysql_store_result(mysql)) mysql_free_result(res);