diff options
Diffstat (limited to 'libmariadb/unittest/libmariadb/async.c')
-rw-r--r-- | libmariadb/unittest/libmariadb/async.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/libmariadb/unittest/libmariadb/async.c b/libmariadb/unittest/libmariadb/async.c new file mode 100644 index 00000000..079f19d8 --- /dev/null +++ b/libmariadb/unittest/libmariadb/async.c @@ -0,0 +1,271 @@ +/* + Copyright 2011 Kristian Nielsen and Monty Program Ab. + + This file is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "my_test.h" +#include "ma_common.h" + + +#ifndef _WIN32 +#include <poll.h> +#else +#include <winsock2.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <mysql.h> + +my_bool skip_async= 0; + +static int test_async(MYSQL *mysql) +{ + int type; + mariadb_get_info(mysql, MARIADB_CONNECTION_PVIO_TYPE, &type); + if (type > MARIADB_CONNECTION_TCP) + { + skip_async= 1; + diag("Asnyc IO not supported"); + } + return OK; +} + +static int +wait_for_mysql(MYSQL *mysql, int status) +{ +#ifdef _WIN32 + fd_set rs, ws, es; + int res; + struct timeval tv, *timeout; + my_socket s= mysql_get_socket(mysql); + FD_ZERO(&rs); + FD_ZERO(&ws); + FD_ZERO(&es); + if (status & MYSQL_WAIT_READ) + FD_SET(s, &rs); + if (status & MYSQL_WAIT_WRITE) + FD_SET(s, &ws); + if (status & MYSQL_WAIT_EXCEPT) + FD_SET(s, &es); + if (status & MYSQL_WAIT_TIMEOUT) + { + tv.tv_sec= mysql_get_timeout_value(mysql); + tv.tv_usec= 0; + timeout= &tv; + } + else + timeout= NULL; + res= select(1, &rs, &ws, &es, timeout); + if (res == 0) + return MYSQL_WAIT_TIMEOUT; + else if (res == SOCKET_ERROR) + { + /* + In a real event framework, we should handle errors and re-try the select. + */ + return MYSQL_WAIT_TIMEOUT; + } + else + { + int status= 0; + if (FD_ISSET(s, &rs)) + status|= MYSQL_WAIT_READ; + if (FD_ISSET(s, &ws)) + status|= MYSQL_WAIT_WRITE; + if (FD_ISSET(s, &es)) + status|= MYSQL_WAIT_EXCEPT; + return status; + } +#else + struct pollfd pfd; + int timeout; + int res= -1; + + pfd.fd= mysql_get_socket(mysql); + pfd.events= + (status & MYSQL_WAIT_READ ? POLLIN : 0) | + (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) | + (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0); + if (status & MYSQL_WAIT_TIMEOUT) + { + timeout= mysql_get_timeout_value_ms(mysql); + } + else + timeout= -1; + do { + res= poll(&pfd, 1, timeout); + } while (res == -1 && errno == EINTR); + if (res == 0) + return MYSQL_WAIT_TIMEOUT; + else if (res < 0) + { + /* + In a real event framework, we should handle EINTR and re-try the poll. + */ + return MYSQL_WAIT_TIMEOUT; + } + else + { + int status= 0; + if (pfd.revents & POLLIN) + status|= MYSQL_WAIT_READ; + if (pfd.revents & POLLOUT) + status|= MYSQL_WAIT_WRITE; + if (pfd.revents & POLLPRI) + status|= MYSQL_WAIT_EXCEPT; + return status; + } +#endif +} + +static int async1(MYSQL *unused __attribute__((unused))) +{ + int err= 0, rc; + MYSQL mysql, *ret; + MYSQL_RES *res; + MYSQL_ROW row; + int status; + uint default_timeout; + int i; + + if (skip_async) + return SKIP; + + for (i=0; i < 100; i++) + { + + mysql_init(&mysql); + rc= mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0); + check_mysql_rc(rc, (MYSQL *)&mysql); + + /* set timeouts to 300 microseconds */ + default_timeout= 3; + mysql_options(&mysql, MYSQL_OPT_READ_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_OPT_WRITE_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp"); + if (force_tls) + mysql_ssl_set(&mysql, NULL, NULL, NULL, NULL,NULL); + + /* Returns 0 when done, else flag for what to wait for when need to block. */ + status= mysql_real_connect_start(&ret, &mysql, hostname, username, password, schema, port, socketname, 0); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_real_connect_cont(&ret, &mysql, status); + } + if (!ret) + { + diag("Error: %s", mysql_error(&mysql)); + FAIL_IF(!ret, "Failed to mysql_real_connect()"); + } + + if (force_tls && !mysql_get_ssl_cipher(&mysql)) + { + diag("Error: No tls connection"); + return FAIL; + } + + status= mysql_real_query_start(&err, &mysql, SL("SHOW STATUS")); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_real_query_cont(&err, &mysql, status); + } + FAIL_IF(err, "mysql_real_query() returns error"); + + /* This method cannot block. */ + res= mysql_use_result(&mysql); + FAIL_IF(!res, "mysql_use_result() returns error"); + + for (;;) + { + status= mysql_fetch_row_start(&row, res); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_fetch_row_cont(&row, res, status); + } + if (!row) + break; + } + FAIL_IF(mysql_errno(&mysql), "Got error while retrieving rows"); + mysql_free_result(res); + + /* + mysql_close() sends a COM_QUIT packet, and so in principle could block + waiting for the socket to accept the data. + In practise, for many applications it will probably be fine to use the + blocking mysql_close(). + */ + status= mysql_close_start(&mysql); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_close_cont(&mysql, status); + } + } + return OK; +} + +static int test_conc131(MYSQL *unused __attribute__((unused))) +{ + int rc; + /* this test needs to run under valgrind */ + MYSQL *mysql; + + if (skip_async) + return SKIP; + + mysql= mysql_init(NULL); + rc= mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0); + check_mysql_rc(rc, mysql); + mysql_close(mysql); + return OK; +} + +static int test_conc129(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (skip_async) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(mysql_close_start(mysql), "No error expected"); + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_async", test_async, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"async1", async1, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc131", test_conc131, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc129", test_conc129, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} |