mirror of
https://github.com/MariaDB/server.git
synced 2026-05-01 20:55:32 +02:00
MWL#192: Non-blocking client API for libmysqlclient.
All client functions that can block on I/O have alternate _start() and _cont() versions that do not block but return control back to the application, which can then issue I/O wait in its own fashion and later call back into the library to continue the operation. Works behind the scenes by spawning a co-routine/fiber to run the blocking operation and suspend it while waiting for I/O. This co-routine/fiber use is invisible to applications. For i368/x86_64 on GCC, uses very fast assembler co-routine support. On Windows uses native Win32 Fibers. Falls back to POSIX ucontext on other platforms. Assembler routines for more platforms are relatively easy to add by extending mysys/my_context.c, eg. similar to the Lua lcoco library. For testing, mysqltest and mysql_client_test are extended with the option --non-blocking-api. This causes the programs to use the non-blocking API for database access. mysql-test-run.pl has a similar option --non-blocking-api that uses this, as well as additional testcases. An example program tests/async_queries.c is included that uses the new non-blocking API with libevent to show how, in a single-threaded program, to issue many queries in parallel against a database. client/async_example.c: Fix const warning ****** Fix bug with wrong timeout value for poll(). include/Makefile.am: Fix missing include for `make dist` include/mysql.h: Add prototypes for all non-blocking API calls. include/mysql.h.pp: Add prototypes for all non-blocking API calls. mysys/my_context.c: Fix type warning for makecontext() function pointer argument. sql-common/mysql_async.c: Fix crashes in the non-blocking API for functions that can take MYSQL argument that is NULL. tests/Makefile.am: Add header file to `make dist` tests/mysql_client_test.c: Replace blocking calls with wrappers around the non-blocking calls, used in mysql_client_test to test the new non-blocking API. tests/nonblock-wrappers.h: Replace blocking calls with wrappers around the non-blocking calls, used in mysql_client_test to test the new non-blocking API.
This commit is contained in:
parent
1a344b87e4
commit
a5b881594d
41 changed files with 4389 additions and 38 deletions
514
tests/nonblock-wrappers.h
Normal file
514
tests/nonblock-wrappers.h
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
/* Copyright (c) 2011 Monty Program Ab
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
Wrappers that re-implement the normal blocking libmysql API calls in terms
|
||||
of the non-blocking API calls and explicit waiting.
|
||||
|
||||
Used to test the non-blocking calls using mysql_client_test.
|
||||
*/
|
||||
|
||||
#ifndef __WIN__
|
||||
#include <poll.h>
|
||||
#else
|
||||
#include <WinSock2.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
Run the appropriate poll() syscall to wait for the event that libmysql
|
||||
requested. Return which event(s) occured.
|
||||
*/
|
||||
static int
|
||||
wait_for_mysql(MYSQL *mysql, int status)
|
||||
{
|
||||
#ifdef __WIN__
|
||||
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)
|
||||
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;
|
||||
|
||||
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= 1000*mysql_get_timeout_value(mysql);
|
||||
else
|
||||
timeout= -1;
|
||||
do {
|
||||
res= poll(&pfd, 1, timeout);
|
||||
/*
|
||||
In a real event framework, we should re-compute the timeout on getting
|
||||
EINTR to account for the time elapsed before the interruption.
|
||||
*/
|
||||
} while (res < 0 && errno == EINTR);
|
||||
if (res == 0)
|
||||
return MYSQL_WAIT_TIMEOUT;
|
||||
else if (res < 0)
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
If WRAP_NONBLOCK_ENABLED is defined, it is a variable that can be used to
|
||||
enable or disable the use of non-blocking API wrappers. If true the
|
||||
non-blocking API will be used, if false the normal blocking API will be
|
||||
called directly.
|
||||
*/
|
||||
#ifdef WRAP_NONBLOCK_ENABLED
|
||||
#define USE_BLOCKING(name__, invoke_blocking__) \
|
||||
if (!(WRAP_NONBLOCK_ENABLED)) return name__ invoke_blocking__;
|
||||
#define USE_BLOCKING_VOID_RETURN(name__, invoke__) \
|
||||
if (!(WRAP_NONBLOCK_ENABLED)) { name__ invoke__; return; }
|
||||
#else
|
||||
#define USE_BLOCKING(name__, invoke_blocking__)
|
||||
#define USE_BLOCKING_VOID_RETURN(name__, invoke__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
I would preferably have declared the wrappers static.
|
||||
However, if we do so, compilers will warn about definitions not used, and
|
||||
with -Werror this breaks compilation :-(
|
||||
*/
|
||||
#define MK_WRAPPER(ret_type__, name__, decl__, invoke__, invoke_blocking__, cont_arg__, mysql__) \
|
||||
ret_type__ wrap_ ## name__ decl__ \
|
||||
{ \
|
||||
ret_type__ res; \
|
||||
int status; \
|
||||
USE_BLOCKING(name__, invoke_blocking__) \
|
||||
status= name__ ## _start invoke__; \
|
||||
while (status) \
|
||||
{ \
|
||||
status= wait_for_mysql(mysql__, status); \
|
||||
status= name__ ## _cont(&res, cont_arg__, status); \
|
||||
} \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#define MK_WRAPPER_VOID_RETURN(name__, decl__, invoke__, cont_arg__, mysql__) \
|
||||
void wrap_ ## name__ decl__ \
|
||||
{ \
|
||||
int status; \
|
||||
USE_BLOCKING_VOID_RETURN(name__, invoke__) \
|
||||
status= name__ ## _start invoke__; \
|
||||
while (status) \
|
||||
{ \
|
||||
status= wait_for_mysql(mysql__, status); \
|
||||
status= name__ ## _cont(cont_arg__, status); \
|
||||
} \
|
||||
}
|
||||
|
||||
MK_WRAPPER(
|
||||
MYSQL *,
|
||||
mysql_real_connect,
|
||||
(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag),
|
||||
(&res, mysql, host, user, passwd, db, port, unix_socket, clientflag),
|
||||
(mysql, host, user, passwd, db, port, unix_socket, clientflag),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_real_query,
|
||||
(MYSQL *mysql, const char *stmt_str, unsigned long length),
|
||||
(&res, mysql, stmt_str, length),
|
||||
(mysql, stmt_str, length),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
MYSQL_ROW,
|
||||
mysql_fetch_row,
|
||||
(MYSQL_RES *result),
|
||||
(&res, result),
|
||||
(result),
|
||||
result,
|
||||
result->handle)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_set_character_set,
|
||||
(MYSQL *mysql, const char *csname),
|
||||
(&res, mysql, csname),
|
||||
(mysql, csname),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_select_db,
|
||||
(MYSQL *mysql, const char *db),
|
||||
(&res, mysql, db),
|
||||
(mysql, db),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_send_query,
|
||||
(MYSQL *mysql, const char *q, unsigned long length),
|
||||
(&res, mysql, q, length),
|
||||
(mysql, q, length),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
MYSQL_RES *,
|
||||
mysql_store_result,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER_VOID_RETURN(
|
||||
mysql_free_result,
|
||||
(MYSQL_RES *result),
|
||||
(result),
|
||||
result,
|
||||
result->handle)
|
||||
|
||||
MK_WRAPPER_VOID_RETURN(
|
||||
mysql_close,
|
||||
(MYSQL *sock),
|
||||
(sock),
|
||||
sock,
|
||||
sock)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_change_user,
|
||||
(MYSQL *mysql, const char *user, const char *passwd, const char *db),
|
||||
(&res, mysql, user, passwd, db),
|
||||
(mysql, user, passwd, db),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_query,
|
||||
(MYSQL *mysql, const char *q),
|
||||
(&res, mysql, q),
|
||||
(mysql, q),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_shutdown,
|
||||
(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level),
|
||||
(&res, mysql, shutdown_level),
|
||||
(mysql, shutdown_level),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_dump_debug_info,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_refresh,
|
||||
(MYSQL *mysql, unsigned int refresh_options),
|
||||
(&res, mysql, refresh_options),
|
||||
(mysql, refresh_options),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_kill,
|
||||
(MYSQL *mysql, unsigned long pid),
|
||||
(&res, mysql, pid),
|
||||
(mysql, pid),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_set_server_option,
|
||||
(MYSQL *mysql, enum enum_mysql_set_option option),
|
||||
(&res, mysql, option),
|
||||
(mysql, option),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_ping,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
const char *,
|
||||
mysql_stat,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
MYSQL_RES *,
|
||||
mysql_list_dbs,
|
||||
(MYSQL *mysql, const char *wild),
|
||||
(&res, mysql, wild),
|
||||
(mysql, wild),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
MYSQL_RES *,
|
||||
mysql_list_tables,
|
||||
(MYSQL *mysql, const char *wild),
|
||||
(&res, mysql, wild),
|
||||
(mysql, wild),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
MYSQL_RES *,
|
||||
mysql_list_processes,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
MYSQL_RES *,
|
||||
mysql_list_fields,
|
||||
(MYSQL *mysql, const char *table, const char *wild),
|
||||
(&res, mysql, table, wild),
|
||||
(mysql, table, wild),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_read_query_result,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_stmt_prepare,
|
||||
(MYSQL_STMT *stmt, const char *query, unsigned long length),
|
||||
(&res, stmt, query, length),
|
||||
(stmt, query, length),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_stmt_execute,
|
||||
(MYSQL_STMT *stmt),
|
||||
(&res, stmt),
|
||||
(stmt),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_stmt_fetch,
|
||||
(MYSQL_STMT *stmt),
|
||||
(&res, stmt),
|
||||
(stmt),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_stmt_store_result,
|
||||
(MYSQL_STMT *stmt),
|
||||
(&res, stmt),
|
||||
(stmt),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_stmt_close,
|
||||
(MYSQL_STMT *stmt),
|
||||
(&res, stmt),
|
||||
(stmt),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_stmt_reset,
|
||||
(MYSQL_STMT *stmt),
|
||||
(&res, stmt),
|
||||
(stmt),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_stmt_free_result,
|
||||
(MYSQL_STMT *stmt),
|
||||
(&res, stmt),
|
||||
(stmt),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_stmt_send_long_data,
|
||||
(MYSQL_STMT *stmt, unsigned int param_number, const char *data, unsigned long length),
|
||||
(&res, stmt, param_number, data, length),
|
||||
(stmt, param_number, data, length),
|
||||
stmt,
|
||||
stmt->mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_commit,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_rollback,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
my_bool,
|
||||
mysql_autocommit,
|
||||
(MYSQL *mysql, my_bool auto_mode),
|
||||
(&res, mysql, auto_mode),
|
||||
(mysql, auto_mode),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
MK_WRAPPER(
|
||||
int,
|
||||
mysql_next_result,
|
||||
(MYSQL *mysql),
|
||||
(&res, mysql),
|
||||
(mysql),
|
||||
mysql,
|
||||
mysql)
|
||||
|
||||
#undef USE_BLOCKING
|
||||
#undef MK_WRAPPER
|
||||
#undef MK_WRAPPER_VOID_RETURN
|
||||
|
||||
|
||||
#define mysql_real_connect wrap_mysql_real_connect
|
||||
#define mysql_real_query wrap_mysql_real_query
|
||||
#define mysql_fetch_row wrap_mysql_fetch_row
|
||||
#define mysql_set_character_set wrap_mysql_set_character_set
|
||||
#define mysql_select_db wrap_mysql_select_db
|
||||
#define mysql_send_query wrap_mysql_send_query
|
||||
#define mysql_store_result wrap_mysql_store_result
|
||||
#define mysql_free_result wrap_mysql_free_result
|
||||
#define mysql_close wrap_mysql_close
|
||||
#define mysql_change_user wrap_mysql_change_user
|
||||
#define mysql_query wrap_mysql_query
|
||||
#define mysql_shutdown wrap_mysql_shutdown
|
||||
#define mysql_dump_debug_info wrap_mysql_dump_debug_info
|
||||
#define mysql_refresh wrap_mysql_refresh
|
||||
#define mysql_kill wrap_mysql_kill
|
||||
#define mysql_set_server_option wrap_mysql_set_server_option
|
||||
#define mysql_ping wrap_mysql_ping
|
||||
#define mysql_stat wrap_mysql_stat
|
||||
#define mysql_list_dbs wrap_mysql_list_dbs
|
||||
#define mysql_list_tables wrap_mysql_list_tables
|
||||
#define mysql_list_processes wrap_mysql_list_processes
|
||||
#define mysql_list_fields wrap_mysql_list_fields
|
||||
#define mysql_read_query_result wrap_mysql_read_query_result
|
||||
#define mysql_stmt_prepare wrap_mysql_stmt_prepare
|
||||
#define mysql_stmt_execute wrap_mysql_stmt_execute
|
||||
#define mysql_stmt_fetch wrap_mysql_stmt_fetch
|
||||
#define mysql_stmt_store_result wrap_mysql_stmt_store_result
|
||||
#define mysql_stmt_close wrap_mysql_stmt_close
|
||||
#define mysql_stmt_reset wrap_mysql_stmt_reset
|
||||
#define mysql_stmt_free_result wrap_mysql_stmt_free_result
|
||||
#define mysql_stmt_send_long_data wrap_mysql_stmt_send_long_data
|
||||
#define mysql_commit wrap_mysql_commit
|
||||
#define mysql_rollback wrap_mysql_rollback
|
||||
#define mysql_autocommit wrap_mysql_autocommit
|
||||
#define mysql_next_result wrap_mysql_next_result
|
||||
Loading…
Add table
Add a link
Reference in a new issue