1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-07 02:42:49 +03:00

Added MariaDB's asnychronous client API.

For more information please visit                                                                                                        
http://mariadb.com/kb/en/non-blocking-api-reference/
This commit is contained in:
Georg Richter
2014-04-08 12:18:08 +02:00
parent 55c5060d4b
commit b55403544e
15 changed files with 3354 additions and 20 deletions

View File

@@ -20,6 +20,11 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* unsupported macros (used by async) */
#define DBUG_SWAP_CODE_STATE(a) {}
#define DBUG_FREE_CODE_STATE(a) {}
#if !defined(DBUG_OFF) && !defined(_lint) #if !defined(DBUG_OFF) && !defined(_lint)
struct _db_stack_frame_ { struct _db_stack_frame_ {
@@ -120,7 +125,6 @@ extern const char* _db_get_func_(void);
#endif #endif
#else /* No debugger */ #else /* No debugger */
#define DBUG_ENTER(a1) #define DBUG_ENTER(a1)
#define DBUG_END() {} #define DBUG_END() {}
#define DBUG_RETURN(a1) return(a1) #define DBUG_RETURN(a1) return(a1)

View File

@@ -31,12 +31,15 @@ typedef struct st_mariadb_db_driver
void *buffer; void *buffer;
} MARIADB_DB_DRIVER; } MARIADB_DB_DRIVER;
struct st_mysql_options_extention { struct mysql_async_context;
struct st_mysql_options_extension {
char *plugin_dir; char *plugin_dir;
char *default_auth; char *default_auth;
char *ssl_crl; char *ssl_crl;
char *ssl_crlpath; char *ssl_crlpath;
char *server_public_key_path; char *server_public_key_path;
struct mysql_async_context *async_context;
HASH connect_attrs; HASH connect_attrs;
size_t connect_attrs_len; size_t connect_attrs_len;
void (*report_progress)(const MYSQL *mysql, void (*report_progress)(const MYSQL *mysql,

232
include/my_context.h Normal file
View File

@@ -0,0 +1,232 @@
/*
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/>.
*/
/*
Simple API for spawning a co-routine, to be used for async libmysqlclient.
Idea is that by implementing this interface using whatever facilities are
available for given platform, we can use the same code for the generic
libmysqlclient-async code.
(This particular implementation uses Posix ucontext swapcontext().)
*/
#ifdef __WIN__
#define MY_CONTEXT_USE_WIN32_FIBERS 1
#elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__x86_64__) && !defined(__ILP32__)
#define MY_CONTEXT_USE_X86_64_GCC_ASM
#elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__i386__)
#define MY_CONTEXT_USE_I386_GCC_ASM
#elif defined(HAVE_UCONTEXT)
#define MY_CONTEXT_USE_UCONTEXT
#else
#define MY_CONTEXT_DISABLE
#endif
#ifdef MY_CONTEXT_USE_WIN32_FIBERS
struct my_context {
void (*user_func)(void *);
void *user_arg;
void *app_fiber;
void *lib_fiber;
int return_value;
#ifndef DBUG_OFF
void *dbug_state;
#endif
};
#endif
#ifdef MY_CONTEXT_USE_UCONTEXT
#include <ucontext.h>
struct my_context {
void (*user_func)(void *);
void *user_data;
void *stack;
size_t stack_size;
ucontext_t base_context;
ucontext_t spawned_context;
int active;
#ifdef HAVE_VALGRIND
unsigned int valgrind_stack_id;
#endif
#ifndef DBUG_OFF
void *dbug_state;
#endif
};
#endif
#ifdef MY_CONTEXT_USE_X86_64_GCC_ASM
#include <stdint.h>
struct my_context {
uint64_t save[9];
void *stack_top;
void *stack_bot;
#ifdef HAVE_VALGRIND
unsigned int valgrind_stack_id;
#endif
#ifndef DBUG_OFF
void *dbug_state;
#endif
};
#endif
#ifdef MY_CONTEXT_USE_I386_GCC_ASM
#include <stdint.h>
struct my_context {
uint64_t save[7];
void *stack_top;
void *stack_bot;
#ifdef HAVE_VALGRIND
unsigned int valgrind_stack_id;
#endif
#ifndef DBUG_OFF
void *dbug_state;
#endif
};
#endif
#ifdef MY_CONTEXT_DISABLE
struct my_context {
int dummy;
};
#endif
/*
Initialize an asynchroneous context object.
Returns 0 on success, non-zero on failure.
*/
extern int my_context_init(struct my_context *c, size_t stack_size);
/* Free an asynchroneous context object, deallocating any resources used. */
extern void my_context_destroy(struct my_context *c);
/*
Spawn an asynchroneous context. The context will run the supplied user
function, passing the supplied user data pointer.
The context must have been initialised with my_context_init() prior to
this call.
The user function may call my_context_yield(), which will cause this
function to return 1. Then later my_context_continue() may be called, which
will resume the asynchroneous context by returning from the previous
my_context_yield() call.
When the user function returns, this function returns 0.
In case of error, -1 is returned.
*/
extern int my_context_spawn(struct my_context *c, void (*f)(void *), void *d);
/*
Suspend an asynchroneous context started with my_context_spawn.
When my_context_yield() is called, execution immediately returns from the
last my_context_spawn() or my_context_continue() call. Then when later
my_context_continue() is called, execution resumes by returning from this
my_context_yield() call.
Returns 0 if ok, -1 in case of error.
*/
extern int my_context_yield(struct my_context *c);
/*
Resume an asynchroneous context. The context was spawned by
my_context_spawn(), and later suspended inside my_context_yield().
The asynchroneous context may be repeatedly suspended with
my_context_yield() and resumed with my_context_continue().
Each time it is suspended, this function returns 1. When the originally
spawned user function returns, this function returns 0.
In case of error, -1 is returned.
*/
extern int my_context_continue(struct my_context *c);
struct mysql_async_context {
/*
This is set to the value that should be returned from foo_start() or
foo_cont() when a call is suspended.
*/
unsigned int events_to_wait_for;
/*
It is also set to the event(s) that triggered when a suspended call is
resumed, eg. whether we woke up due to connection completed or timeout
in mysql_real_connect_cont().
*/
unsigned int events_occured;
/*
This is set to the result of the whole asynchronous operation when it
completes. It uses a union, as different calls have different return
types.
*/
union {
void *r_ptr;
const void *r_const_ptr;
int r_int;
my_bool r_my_bool;
} ret_result;
/*
The timeout value (in millisecods), for suspended calls that need to wake
up on a timeout (eg. mysql_real_connect_start().
*/
unsigned int timeout_value;
/*
This flag is set when we are executing inside some asynchronous call
foo_start() or foo_cont(). It is used to decide whether to use the
synchronous or asynchronous version of calls that may block such as
recv().
Note that this flag is not set when a call is suspended, eg. after
returning from foo_start() and before re-entering foo_cont().
*/
my_bool active;
/*
This flag is set when an asynchronous operation is in progress, but
suspended. Ie. it is set when foo_start() or foo_cont() returns because
the operation needs to block, suspending the operation.
It is used to give an error (rather than crash) if the application
attempts to call some foo_cont() method when no suspended operation foo is
in progress.
*/
my_bool suspended;
/*
If non-NULL, this is a pointer to a callback hook that will be invoked with
the user data argument just before the context is suspended, and just after
it is resumed.
*/
void (*suspend_resume_hook)(my_bool suspend, void *user_data);
void *suspend_resume_hook_user_data;
/*
This is used to save the execution contexts so that we can suspend an
operation and switch back to the application context, to resume the
suspended context later when the application re-invokes us with
foo_cont().
*/
struct my_context async_context;
};

View File

@@ -364,13 +364,14 @@ typedef int (*qsort_cmp)(const void *,const void *);
#else #else
#define qsort_t RETQSORTTYPE /* Broken GCC cant handle typedef !!!! */ #define qsort_t RETQSORTTYPE /* Broken GCC cant handle typedef !!!! */
#endif #endif
#ifdef HAVE_mit_thread typedef SOCKET_SIZE_TYPE size_socket;
#define size_socket socklen_t /* Type of last arg to accept */
#else #ifndef SOCKOPT_OPTLEN_TYPE
#ifdef HAVE_SYS_SOCKET_H #define SOCKOPT_OPTLEN_TYPE size_socket
#include <sys/socket.h>
#endif #endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif #endif
#ifndef SOCKOPT_OPTLEN_TYPE #ifndef SOCKOPT_OPTLEN_TYPE
@@ -1085,6 +1086,22 @@ do { doubleget_union _tmp; \
#endif #endif
#endif #endif
#if SIZEOF_CHARP == SIZEOF_INT
typedef unsigned int intptr;
#elif SIZEOF_CHARP == SIZEOF_LONG
typedef unsigned long intptr;
#elif SIZEOF_CHARP == SIZEOF_LONG_LONG
typedef unsigned long long intptr;
#else
#error sizeof(void *) is not sizeof(int, long or long long)
#endif
#ifdef _WIN32
#define IF_WIN(A,B) A
#else
#define IF_WIN(A,B) B
#endif
#ifndef RTLD_NOW #ifndef RTLD_NOW
#define RTLD_NOW 1 #define RTLD_NOW 1
#endif #endif

View File

@@ -144,6 +144,10 @@ typedef long longlong;
strncpy((a)->net.last_error, (d) ? (d) : ER((b)), sizeof((a)->net.last_error));\ strncpy((a)->net.last_error, (d) ? (d) : ER((b)), sizeof((a)->net.last_error));\
} }
/* For mysql_async.c */
#define set_mysql_error(A,B,C) SET_CLIENT_ERROR((A),(B),(C),0)
#define unknown_sqlstate SQLSTATE_UNKNOWN
#define CLEAR_CLIENT_ERROR(a) \ #define CLEAR_CLIENT_ERROR(a) \
{ \ { \
(a)->net.last_errno= 0;\ (a)->net.last_errno= 0;\
@@ -210,6 +214,7 @@ typedef long longlong;
/* MariaDB specific */ /* MariaDB specific */
MYSQL_PROGRESS_CALLBACK=5999, MYSQL_PROGRESS_CALLBACK=5999,
MYSQL_OPT_NONBLOCK,
MYSQL_DATABASE_DRIVER=7000 MYSQL_DATABASE_DRIVER=7000
}; };
@@ -229,7 +234,7 @@ typedef long longlong;
MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY
}; };
struct st_mysql_options_extention; struct st_mysql_options_extension;
struct st_mysql_options { struct st_mysql_options {
unsigned int connect_timeout, read_timeout, write_timeout; unsigned int connect_timeout, read_timeout, write_timeout;
@@ -258,7 +263,7 @@ struct st_mysql_options {
void (*local_infile_end)(void *); void (*local_infile_end)(void *);
int (*local_infile_error)(void *, char *, unsigned int); int (*local_infile_error)(void *, char *, unsigned int);
void *local_infile_userdata; void *local_infile_userdata;
struct st_mysql_options_extention *extension; struct st_mysql_options_extension *extension;
}; };
typedef struct st_mysql { typedef struct st_mysql {
@@ -330,6 +335,12 @@ typedef struct st_mysql_time
#define AUTO_SEC_PART_DIGITS 31 #define AUTO_SEC_PART_DIGITS 31
#define SEC_PART_DIGITS 6 #define SEC_PART_DIGITS 6
/* Ansynchronous API constants */
#define MYSQL_WAIT_READ 1
#define MYSQL_WAIT_WRITE 2
#define MYSQL_WAIT_EXCEPT 4
#define MYSQL_WAIT_TIMEOUT 8
typedef struct character_set typedef struct character_set
{ {
unsigned int number; /* character set number */ unsigned int number; /* character set number */
@@ -479,6 +490,9 @@ size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, CHARSE
int STDCALL mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...); int STDCALL mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...);
MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void); MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void);
ulong STDCALL mysql_hex_string(char *to, const char *from, unsigned long len); ulong STDCALL mysql_hex_string(char *to, const char *from, unsigned long len);
my_socket STDCALL mysql_get_socket(const MYSQL *mysql);
unsigned int STDCALL mysql_get_timeout_value(const MYSQL *mysql);
unsigned int STDCALL mysql_get_timeout_value_ms(const MYSQL *mysql);
#include <my_stmt.h> #include <my_stmt.h>

38
include/mysql_async.h Normal file
View File

@@ -0,0 +1,38 @@
/* Copyright (C) 2012 MariaDB Services and Kristian Nielsen
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 */
/* Common definitions for MariaDB non-blocking client library. */
#ifndef MYSQL_ASYNC_H
#define MYSQL_ASYNC_H
extern int my_connect_async(struct mysql_async_context *b, my_socket fd,
const struct sockaddr *name, uint namelen,
int vio_timeout);
extern ssize_t my_recv_async(struct mysql_async_context *b, int fd,
unsigned char *buf, size_t size, int timeout);
extern ssize_t my_send_async(struct mysql_async_context *b, int fd,
const unsigned char *buf, size_t size,
int timeout);
extern my_bool my_io_wait_async(struct mysql_async_context *b,
enum enum_vio_io_event event, int timeout);
#ifdef HAVE_OPENSSL
extern int my_ssl_read_async(struct mysql_async_context *b, SSL *ssl,
void *buf, int size);
extern int my_ssl_write_async(struct mysql_async_context *b, SSL *ssl,
const void *buf, int size);
#endif
#endif /* MYSQL_ASYNC_H */

View File

@@ -35,6 +35,13 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#endif #endif
enum enum_vio_io_event
{
VIO_IO_EVENT_READ,
VIO_IO_EVENT_WRITE,
VIO_IO_EVENT_CONNECT
};
/* Simple vio interface in C; The functions are implemented in violite.c */ /* Simple vio interface in C; The functions are implemented in violite.c */
#ifdef __cplusplus #ifdef __cplusplus
@@ -138,6 +145,8 @@ struct st_vio
int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */ int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */
struct sockaddr_in local; /* Local internet address */ struct sockaddr_in local; /* Local internet address */
struct sockaddr_in remote; /* Remote internet address */ struct sockaddr_in remote; /* Remote internet address */
struct mysql_async_context *async_context; /* For non-blocking API */
enum enum_vio_type type; /* Type of connection */ enum enum_vio_type type; /* Type of connection */
char desc[30]; /* String description */ char desc[30]; /* String description */
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL

View File

@@ -53,6 +53,7 @@ mf_wcomp.c
mulalloc.c mulalloc.c
my_alloc.c my_alloc.c
my_compress.c my_compress.c
my_context.c
my_div.c my_div.c
my_error.c my_error.c
my_fopen.c my_fopen.c
@@ -74,6 +75,7 @@ my_static.c
my_symlink.c my_symlink.c
my_thr_init.c my_thr_init.c
my_write.c my_write.c
mysql_async.c
password.c password.c
str2int.c str2int.c
strcend.c strcend.c

View File

@@ -28,6 +28,7 @@
#include <m_string.h> #include <m_string.h>
#include <m_ctype.h> #include <m_ctype.h>
#include <ma_common.h> #include <ma_common.h>
#include "my_context.h"
#include "mysql.h" #include "mysql.h"
#include "mysql_version.h" #include "mysql_version.h"
#include "mysqld_error.h" #include "mysqld_error.h"
@@ -69,6 +70,8 @@
#endif #endif
#include <ma_dyncol.h> #include <ma_dyncol.h>
#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15)
#undef max_allowed_packet #undef max_allowed_packet
#undef net_buffer_length #undef net_buffer_length
extern ulong max_allowed_packet; /* net.c */ extern ulong max_allowed_packet; /* net.c */
@@ -119,7 +122,6 @@ struct st_mysql_methods MARIADB_DEFAULT_METHODS;
#include <mysql/client_plugin.h> #include <mysql/client_plugin.h>
#define native_password_plugin_name "mysql_native_password" #define native_password_plugin_name "mysql_native_password"
const char *unknown_sqlstate= "HY000";
static void end_server(MYSQL *mysql); static void end_server(MYSQL *mysql);
static void mysql_close_memory(MYSQL *mysql); static void mysql_close_memory(MYSQL *mysql);
@@ -841,8 +843,8 @@ enum option_val
#define CHECK_OPT_EXTENSION_SET(OPTS)\ #define CHECK_OPT_EXTENSION_SET(OPTS)\
if (!(OPTS)->extension) \ if (!(OPTS)->extension) \
(OPTS)->extension= (struct st_mysql_options_extention *) \ (OPTS)->extension= (struct st_mysql_options_extension *) \
my_malloc(sizeof(struct st_mysql_options_extention), \ my_malloc(sizeof(struct st_mysql_options_extension), \
MYF(MY_WME | MY_ZEROFILL)); \ MYF(MY_WME | MY_ZEROFILL)); \
#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \ #define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \
@@ -1578,6 +1580,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
if (connect2(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr), if (connect2(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
mysql->options.connect_timeout) <0) mysql->options.connect_timeout) <0)
{ {
printf("err\n");
DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno)); DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno));
my_set_error(mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONNECTION_ERROR), my_set_error(mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONNECTION_ERROR),
unix_socket, socket_errno); unix_socket, socket_errno);
@@ -2188,7 +2191,7 @@ void my_set_error(MYSQL *mysql,
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
void mthd_my_close(MYSQL *mysql) void STDCALL mysql_close_slow_part(MYSQL *mysql)
{ {
if (mysql->net.vio) if (mysql->net.vio)
{ {
@@ -2813,7 +2816,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
{ {
va_list ap; va_list ap;
void *arg1; void *arg1;
struct mysql_async_context *ctxt;
size_t stacksize;
DBUG_ENTER("mysql_option"); DBUG_ENTER("mysql_option");
DBUG_PRINT("enter",("option: %d",(int) option)); DBUG_PRINT("enter",("option: %d",(int) option));
@@ -2881,8 +2885,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
break; break;
case MYSQL_PROGRESS_CALLBACK: case MYSQL_PROGRESS_CALLBACK:
if (!mysql->options.extension) if (!mysql->options.extension)
mysql->options.extension= (struct st_mysql_options_extention *) mysql->options.extension= (struct st_mysql_options_extension *)
my_malloc(sizeof(struct st_mysql_options_extention), my_malloc(sizeof(struct st_mysql_options_extension),
MYF(MY_WME | MY_ZEROFILL)); MYF(MY_WME | MY_ZEROFILL));
if (mysql->options.extension) if (mysql->options.extension)
mysql->options.extension->report_progress= mysql->options.extension->report_progress=
@@ -2901,8 +2905,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
MYSQL_CLIENT_DB_PLUGIN))) MYSQL_CLIENT_DB_PLUGIN)))
break; break;
if (!mysql->options.extension) if (!mysql->options.extension)
mysql->options.extension= (struct st_mysql_options_extention *) mysql->options.extension= (struct st_mysql_options_extension *)
my_malloc(sizeof(struct st_mysql_options_extention), my_malloc(sizeof(struct st_mysql_options_extension),
MYF(MY_WME | MY_ZEROFILL)); MYF(MY_WME | MY_ZEROFILL));
if (!mysql->options.extension->db_driver) if (!mysql->options.extension->db_driver)
mysql->options.extension->db_driver= (MARIADB_DB_DRIVER *) mysql->options.extension->db_driver= (MARIADB_DB_DRIVER *)
@@ -2917,6 +2921,48 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
} }
} }
break; break;
case MYSQL_OPT_NONBLOCK:
if (mysql->options.extension &&
(ctxt = mysql->options.extension->async_context) != 0)
{
/*
We must not allow changing the stack size while a non-blocking call is
suspended (as the stack is then in use).
*/
if (ctxt->suspended)
DBUG_RETURN(1);
my_context_destroy(&ctxt->async_context);
my_free((gptr)ctxt, MYF(0));
}
if (!(ctxt= (struct mysql_async_context *)
my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL))))
{
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
DBUG_RETURN(1);
}
stacksize= 0;
if (arg1)
stacksize= *(const size_t *)arg1;
if (!stacksize)
stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE;
if (my_context_init(&ctxt->async_context, stacksize))
{
my_free((gptr)ctxt, MYF(0));
DBUG_RETURN(1);
}
if (!mysql->options.extension)
if(!(mysql->options.extension= (struct st_mysql_options_extension *)
my_malloc(sizeof(struct st_mysql_options_extension),
MYF(MY_WME | MY_ZEROFILL))))
{
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
DBUG_RETURN(1);
}
mysql->options.extension->async_context= ctxt;
if (mysql->net.vio)
mysql->net.vio->async_context= ctxt;
break;
case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
if (*(uint *)arg1) if (*(uint *)arg1)
mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT; mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT;
@@ -3431,6 +3477,14 @@ mysql_get_parameters(void)
return &mariadb_internal_parameters; return &mariadb_internal_parameters;
} }
my_socket STDCALL
mysql_get_socket(const MYSQL *mysql)
{
if (mysql->net.vio)
return vio_fd(mysql->net.vio);
return INVALID_SOCKET;
}
/* /*
* Default methods for a connection. These methods are * Default methods for a connection. These methods are
* stored in mysql->methods and can be overwritten by * stored in mysql->methods and can be overwritten by
@@ -3440,7 +3494,7 @@ struct st_mysql_methods MARIADB_DEFAULT_METHODS = {
/* open a connection */ /* open a connection */
mthd_my_real_connect, mthd_my_real_connect,
/* close connection */ /* close connection */
mthd_my_close, mysql_close_slow_part,
/* send command to server */ /* send command to server */
mthd_my_send_cmd, mthd_my_send_cmd,
/* skip result set */ /* skip result set */

View File

@@ -39,6 +39,9 @@ EXPORTS
mysql_get_server_info mysql_get_server_info
mysql_get_client_version mysql_get_client_version
mysql_get_ssl_cipher mysql_get_ssl_cipher
mysql_get_socket
mysql_get_timeout_value
mysql_get_timeout_value_ms
mysql_info mysql_info
mysql_init mysql_init
mysql_insert_id mysql_insert_id

759
libmariadb/my_context.c Normal file
View File

@@ -0,0 +1,759 @@
/*
Copyright 2011, 2012 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/>.
*/
/*
Implementation of async context spawning using Posix ucontext and
swapcontext().
*/
#include "mysys_priv.h"
#include "m_string.h"
#include "my_context.h"
#ifdef HAVE_VALGRIND
#include <valgrind/valgrind.h>
#endif
#ifdef MY_CONTEXT_USE_UCONTEXT
/*
The makecontext() only allows to pass integers into the created context :-(
We want to pass pointers, so we do it this kinda hackish way.
Anyway, it should work everywhere, and at least it does not break strict
aliasing.
*/
union pass_void_ptr_as_2_int {
int a[2];
void *p;
};
/*
We use old-style function definition here, as this is passed to
makecontext(). And the type of the makecontext() argument does not match
the actual type (as the actual type can differ from call to call).
*/
static void
my_context_spawn_internal(i0, i1)
int i0, i1;
{
int err;
struct my_context *c;
union pass_void_ptr_as_2_int u;
u.a[0]= i0;
u.a[1]= i1;
c= (struct my_context *)u.p;
(*c->user_func)(c->user_data);
c->active= 0;
err= setcontext(&c->base_context);
fprintf(stderr, "Aieie, setcontext() failed: %d (errno=%d)\n", err, errno);
}
int
my_context_continue(struct my_context *c)
{
int err;
if (!c->active)
return 0;
DBUG_SWAP_CODE_STATE(&c->dbug_state);
err= swapcontext(&c->base_context, &c->spawned_context);
DBUG_SWAP_CODE_STATE(&c->dbug_state);
if (err)
{
fprintf(stderr, "Aieie, swapcontext() failed: %d (errno=%d)\n",
err, errno);
return -1;
}
return c->active;
}
int
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
{
int err;
union pass_void_ptr_as_2_int u;
err= getcontext(&c->spawned_context);
if (err)
return -1;
c->spawned_context.uc_stack.ss_sp= c->stack;
c->spawned_context.uc_stack.ss_size= c->stack_size;
c->spawned_context.uc_link= NULL;
c->user_func= f;
c->user_data= d;
c->active= 1;
u.p= c;
makecontext(&c->spawned_context, my_context_spawn_internal, 2,
u.a[0], u.a[1]);
return my_context_continue(c);
}
int
my_context_yield(struct my_context *c)
{
int err;
if (!c->active)
return -1;
err= swapcontext(&c->spawned_context, &c->base_context);
if (err)
return -1;
return 0;
}
int
my_context_init(struct my_context *c, size_t stack_size)
{
#if SIZEOF_CHARP > SIZEOF_INT*2
#error Error: Unable to store pointer in 2 ints on this architecture
#endif
bzero(c, sizeof(*c));
if (!(c->stack= malloc(stack_size)))
return -1; /* Out of memory */
c->stack_size= stack_size;
#ifdef HAVE_VALGRIND
c->valgrind_stack_id=
VALGRIND_STACK_REGISTER(c->stack, ((unsigned char *)(c->stack))+stack_size);
#endif
return 0;
}
void
my_context_destroy(struct my_context *c)
{
if (c->stack)
{
#ifdef HAVE_VALGRIND
VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
#endif
free(c->stack);
}
DBUG_FREE_CODE_STATE(&c->dbug_state);
}
#endif /* MY_CONTEXT_USE_UCONTEXT */
#ifdef MY_CONTEXT_USE_X86_64_GCC_ASM
/*
GCC-amd64 implementation of my_context.
This is slightly optimized in the common case where we never yield
(eg. fetch next row and it is already fully received in buffer). In this
case we do not need to restore registers at return (though we still need to
save them as we cannot know if we will yield or not in advance).
*/
#include <stdint.h>
#include <stdlib.h>
/*
Layout of saved registers etc.
Since this is accessed through gcc inline assembler, it is simpler to just
use numbers than to try to define nice constants or structs.
0 0 %rsp
1 8 %rbp
2 16 %rbx
3 24 %r12
4 32 %r13
5 40 %r14
6 48 %r15
7 56 %rip for done
8 64 %rip for yield/continue
*/
int
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
{
int ret;
DBUG_SWAP_CODE_STATE(&c->dbug_state);
/*
There are 6 callee-save registers we need to save and restore when
suspending and continuing, plus stack pointer %rsp and instruction pointer
%rip.
However, if we never suspend, the user-supplied function will in any case
restore the 6 callee-save registers, so we can avoid restoring them in
this case.
*/
__asm__ __volatile__
(
"movq %%rsp, (%[save])\n\t"
"movq %[stack], %%rsp\n\t"
#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
/*
This emits a DWARF DW_CFA_undefined directive to make the return address
undefined. This indicates that this is the top of the stack frame, and
helps tools that use DWARF stack unwinding to obtain stack traces.
(I use numeric constant to avoid a dependency on libdwarf includes).
*/
".cfi_escape 0x07, 16\n\t"
#endif
"movq %%rbp, 8(%[save])\n\t"
"movq %%rbx, 16(%[save])\n\t"
"movq %%r12, 24(%[save])\n\t"
"movq %%r13, 32(%[save])\n\t"
"movq %%r14, 40(%[save])\n\t"
"movq %%r15, 48(%[save])\n\t"
"leaq 1f(%%rip), %%rax\n\t"
"leaq 2f(%%rip), %%rcx\n\t"
"movq %%rax, 56(%[save])\n\t"
"movq %%rcx, 64(%[save])\n\t"
/*
Constraint below puts the argument to the user function into %rdi, as
needed for the calling convention.
*/
"callq *%[f]\n\t"
"jmpq *56(%[save])\n"
/*
Come here when operation is done.
We do not need to restore callee-save registers, as the called function
will do this for us if needed.
*/
"1:\n\t"
"movq (%[save]), %%rsp\n\t"
"xorl %[ret], %[ret]\n\t"
"jmp 3f\n"
/* Come here when operation was suspended. */
"2:\n\t"
"movl $1, %[ret]\n"
"3:\n"
: [ret] "=a" (ret),
[f] "+S" (f),
/* Need this in %rdi to follow calling convention. */
[d] "+D" (d)
: [stack] "a" (c->stack_top),
/* Need this in callee-save register to preserve in function call. */
[save] "b" (&c->save[0])
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"
);
DBUG_SWAP_CODE_STATE(&c->dbug_state);
return ret;
}
int
my_context_continue(struct my_context *c)
{
int ret;
DBUG_SWAP_CODE_STATE(&c->dbug_state);
__asm__ __volatile__
(
"movq (%[save]), %%rax\n\t"
"movq %%rsp, (%[save])\n\t"
"movq %%rax, %%rsp\n\t"
"movq 8(%[save]), %%rax\n\t"
"movq %%rbp, 8(%[save])\n\t"
"movq %%rax, %%rbp\n\t"
"movq 24(%[save]), %%rax\n\t"
"movq %%r12, 24(%[save])\n\t"
"movq %%rax, %%r12\n\t"
"movq 32(%[save]), %%rax\n\t"
"movq %%r13, 32(%[save])\n\t"
"movq %%rax, %%r13\n\t"
"movq 40(%[save]), %%rax\n\t"
"movq %%r14, 40(%[save])\n\t"
"movq %%rax, %%r14\n\t"
"movq 48(%[save]), %%rax\n\t"
"movq %%r15, 48(%[save])\n\t"
"movq %%rax, %%r15\n\t"
"leaq 1f(%%rip), %%rax\n\t"
"leaq 2f(%%rip), %%rcx\n\t"
"movq %%rax, 56(%[save])\n\t"
"movq 64(%[save]), %%rax\n\t"
"movq %%rcx, 64(%[save])\n\t"
"movq 16(%[save]), %%rcx\n\t"
"movq %%rbx, 16(%[save])\n\t"
"movq %%rcx, %%rbx\n\t"
"jmpq *%%rax\n"
/*
Come here when operation is done.
Be sure to use the same callee-save register for %[save] here and in
my_context_spawn(), so we preserve the value correctly at this point.
*/
"1:\n\t"
"movq (%[save]), %%rsp\n\t"
"movq 8(%[save]), %%rbp\n\t"
/* %rbx is preserved from my_context_spawn() in this case. */
"movq 24(%[save]), %%r12\n\t"
"movq 32(%[save]), %%r13\n\t"
"movq 40(%[save]), %%r14\n\t"
"movq 48(%[save]), %%r15\n\t"
"xorl %[ret], %[ret]\n\t"
"jmp 3f\n"
/* Come here when operation is suspended. */
"2:\n\t"
"movl $1, %[ret]\n"
"3:\n"
: [ret] "=a" (ret)
: /* Need this in callee-save register to preserve in function call. */
[save] "b" (&c->save[0])
: "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory", "cc"
);
DBUG_SWAP_CODE_STATE(&c->dbug_state);
return ret;
}
int
my_context_yield(struct my_context *c)
{
uint64_t *save= &c->save[0];
__asm__ __volatile__
(
"movq (%[save]), %%rax\n\t"
"movq %%rsp, (%[save])\n\t"
"movq %%rax, %%rsp\n\t"
"movq 8(%[save]), %%rax\n\t"
"movq %%rbp, 8(%[save])\n\t"
"movq %%rax, %%rbp\n\t"
"movq 16(%[save]), %%rax\n\t"
"movq %%rbx, 16(%[save])\n\t"
"movq %%rax, %%rbx\n\t"
"movq 24(%[save]), %%rax\n\t"
"movq %%r12, 24(%[save])\n\t"
"movq %%rax, %%r12\n\t"
"movq 32(%[save]), %%rax\n\t"
"movq %%r13, 32(%[save])\n\t"
"movq %%rax, %%r13\n\t"
"movq 40(%[save]), %%rax\n\t"
"movq %%r14, 40(%[save])\n\t"
"movq %%rax, %%r14\n\t"
"movq 48(%[save]), %%rax\n\t"
"movq %%r15, 48(%[save])\n\t"
"movq %%rax, %%r15\n\t"
"movq 64(%[save]), %%rax\n\t"
"leaq 1f(%%rip), %%rcx\n\t"
"movq %%rcx, 64(%[save])\n\t"
"jmpq *%%rax\n"
"1:\n"
: [save] "+D" (save)
:
: "rax", "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"
);
return 0;
}
int
my_context_init(struct my_context *c, size_t stack_size)
{
bzero(c, sizeof(*c));
if (!(c->stack_bot= malloc(stack_size)))
return -1; /* Out of memory */
/*
The x86_64 ABI specifies 16-byte stack alignment.
Also put two zero words at the top of the stack.
*/
c->stack_top= (void *)
(( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
bzero(c->stack_top, 16);
#ifdef HAVE_VALGRIND
c->valgrind_stack_id=
VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
#endif
return 0;
}
void
my_context_destroy(struct my_context *c)
{
if (c->stack_bot)
{
free(c->stack_bot);
#ifdef HAVE_VALGRIND
VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
#endif
}
DBUG_FREE_CODE_STATE(&c->dbug_state);
}
#endif /* MY_CONTEXT_USE_X86_64_GCC_ASM */
#ifdef MY_CONTEXT_USE_I386_GCC_ASM
/*
GCC-i386 implementation of my_context.
This is slightly optimized in the common case where we never yield
(eg. fetch next row and it is already fully received in buffer). In this
case we do not need to restore registers at return (though we still need to
save them as we cannot know if we will yield or not in advance).
*/
#include <stdint.h>
#include <stdlib.h>
/*
Layout of saved registers etc.
Since this is accessed through gcc inline assembler, it is simpler to just
use numbers than to try to define nice constants or structs.
0 0 %esp
1 4 %ebp
2 8 %ebx
3 12 %esi
4 16 %edi
5 20 %eip for done
6 24 %eip for yield/continue
*/
int
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
{
int ret;
DBUG_SWAP_CODE_STATE(&c->dbug_state);
/*
There are 4 callee-save registers we need to save and restore when
suspending and continuing, plus stack pointer %esp and instruction pointer
%eip.
However, if we never suspend, the user-supplied function will in any case
restore the 4 callee-save registers, so we can avoid restoring them in
this case.
*/
__asm__ __volatile__
(
"movl %%esp, (%[save])\n\t"
"movl %[stack], %%esp\n\t"
#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
/*
This emits a DWARF DW_CFA_undefined directive to make the return address
undefined. This indicates that this is the top of the stack frame, and
helps tools that use DWARF stack unwinding to obtain stack traces.
(I use numeric constant to avoid a dependency on libdwarf includes).
*/
".cfi_escape 0x07, 8\n\t"
#endif
/* Push the parameter on the stack. */
"pushl %[d]\n\t"
"movl %%ebp, 4(%[save])\n\t"
"movl %%ebx, 8(%[save])\n\t"
"movl %%esi, 12(%[save])\n\t"
"movl %%edi, 16(%[save])\n\t"
/* Get label addresses in -fPIC-compatible way (no pc-relative on 32bit) */
"call 1f\n"
"1:\n\t"
"popl %%eax\n\t"
"addl $(2f-1b), %%eax\n\t"
"movl %%eax, 20(%[save])\n\t"
"addl $(3f-2f), %%eax\n\t"
"movl %%eax, 24(%[save])\n\t"
"call *%[f]\n\t"
"jmp *20(%[save])\n"
/*
Come here when operation is done.
We do not need to restore callee-save registers, as the called function
will do this for us if needed.
*/
"2:\n\t"
"movl (%[save]), %%esp\n\t"
"xorl %[ret], %[ret]\n\t"
"jmp 4f\n"
/* Come here when operation was suspended. */
"3:\n\t"
"movl $1, %[ret]\n"
"4:\n"
: [ret] "=a" (ret),
[f] "+c" (f),
[d] "+d" (d)
: [stack] "a" (c->stack_top),
/* Need this in callee-save register to preserve across function call. */
[save] "D" (&c->save[0])
: "memory", "cc"
);
DBUG_SWAP_CODE_STATE(&c->dbug_state);
return ret;
}
int
my_context_continue(struct my_context *c)
{
int ret;
DBUG_SWAP_CODE_STATE(&c->dbug_state);
__asm__ __volatile__
(
"movl (%[save]), %%eax\n\t"
"movl %%esp, (%[save])\n\t"
"movl %%eax, %%esp\n\t"
"movl 4(%[save]), %%eax\n\t"
"movl %%ebp, 4(%[save])\n\t"
"movl %%eax, %%ebp\n\t"
"movl 8(%[save]), %%eax\n\t"
"movl %%ebx, 8(%[save])\n\t"
"movl %%eax, %%ebx\n\t"
"movl 12(%[save]), %%eax\n\t"
"movl %%esi, 12(%[save])\n\t"
"movl %%eax, %%esi\n\t"
"movl 24(%[save]), %%eax\n\t"
"call 1f\n"
"1:\n\t"
"popl %%ecx\n\t"
"addl $(2f-1b), %%ecx\n\t"
"movl %%ecx, 20(%[save])\n\t"
"addl $(3f-2f), %%ecx\n\t"
"movl %%ecx, 24(%[save])\n\t"
/* Must restore %edi last as it is also our %[save] register. */
"movl 16(%[save]), %%ecx\n\t"
"movl %%edi, 16(%[save])\n\t"
"movl %%ecx, %%edi\n\t"
"jmp *%%eax\n"
/*
Come here when operation is done.
Be sure to use the same callee-save register for %[save] here and in
my_context_spawn(), so we preserve the value correctly at this point.
*/
"2:\n\t"
"movl (%[save]), %%esp\n\t"
"movl 4(%[save]), %%ebp\n\t"
"movl 8(%[save]), %%ebx\n\t"
"movl 12(%[save]), %%esi\n\t"
"movl 16(%[save]), %%edi\n\t"
"xorl %[ret], %[ret]\n\t"
"jmp 4f\n"
/* Come here when operation is suspended. */
"3:\n\t"
"movl $1, %[ret]\n"
"4:\n"
: [ret] "=a" (ret)
: /* Need this in callee-save register to preserve in function call. */
[save] "D" (&c->save[0])
: "ecx", "edx", "memory", "cc"
);
DBUG_SWAP_CODE_STATE(&c->dbug_state);
return ret;
}
int
my_context_yield(struct my_context *c)
{
uint64_t *save= &c->save[0];
__asm__ __volatile__
(
"movl (%[save]), %%eax\n\t"
"movl %%esp, (%[save])\n\t"
"movl %%eax, %%esp\n\t"
"movl 4(%[save]), %%eax\n\t"
"movl %%ebp, 4(%[save])\n\t"
"movl %%eax, %%ebp\n\t"
"movl 8(%[save]), %%eax\n\t"
"movl %%ebx, 8(%[save])\n\t"
"movl %%eax, %%ebx\n\t"
"movl 12(%[save]), %%eax\n\t"
"movl %%esi, 12(%[save])\n\t"
"movl %%eax, %%esi\n\t"
"movl 16(%[save]), %%eax\n\t"
"movl %%edi, 16(%[save])\n\t"
"movl %%eax, %%edi\n\t"
"movl 24(%[save]), %%eax\n\t"
"call 1f\n"
"1:\n\t"
"popl %%ecx\n\t"
"addl $(2f-1b), %%ecx\n\t"
"movl %%ecx, 24(%[save])\n\t"
"jmp *%%eax\n"
"2:\n"
: [save] "+d" (save)
:
: "eax", "ecx", "memory", "cc"
);
return 0;
}
int
my_context_init(struct my_context *c, size_t stack_size)
{
bzero(c, sizeof(*c));
if (!(c->stack_bot= malloc(stack_size)))
return -1; /* Out of memory */
c->stack_top= (void *)
(( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
bzero(c->stack_top, 16);
#ifdef HAVE_VALGRIND
c->valgrind_stack_id=
VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
#endif
return 0;
}
void
my_context_destroy(struct my_context *c)
{
if (c->stack_bot)
{
free(c->stack_bot);
#ifdef HAVE_VALGRIND
VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
#endif
}
DBUG_FREE_CODE_STATE(&c->dbug_state);
}
#endif /* MY_CONTEXT_USE_I386_GCC_ASM */
#ifdef MY_CONTEXT_USE_WIN32_FIBERS
int
my_context_yield(struct my_context *c)
{
c->return_value= 1;
SwitchToFiber(c->app_fiber);
return 0;
}
static void WINAPI
my_context_trampoline(void *p)
{
struct my_context *c= (struct my_context *)p;
/*
Reuse the Fiber by looping infinitely, each time we are scheduled we
spawn the appropriate function and switch back when it is done.
This way we avoid the overhead of CreateFiber() for every asynchroneous
operation.
*/
for(;;)
{
(*(c->user_func))(c->user_arg);
c->return_value= 0;
SwitchToFiber(c->app_fiber);
}
}
int
my_context_init(struct my_context *c, size_t stack_size)
{
bzero(c, sizeof(*c));
c->lib_fiber= CreateFiber(stack_size, my_context_trampoline, c);
if (c->lib_fiber)
return 0;
return -1;
}
void
my_context_destroy(struct my_context *c)
{
DBUG_FREE_CODE_STATE(&c->dbug_state);
if (c->lib_fiber)
{
DeleteFiber(c->lib_fiber);
c->lib_fiber= NULL;
}
}
int
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
{
void *current_fiber;
c->user_func= f;
c->user_arg= d;
/*
This seems to be a common trick to run ConvertThreadToFiber() only on the
first occurence in a thread, in a way that works on multiple Windows
versions.
*/
current_fiber= GetCurrentFiber();
if (current_fiber == NULL || current_fiber == (void *)0x1e00)
current_fiber= ConvertThreadToFiber(c);
c->app_fiber= current_fiber;
DBUG_SWAP_CODE_STATE(&c->dbug_state);
SwitchToFiber(c->lib_fiber);
DBUG_SWAP_CODE_STATE(&c->dbug_state);
return c->return_value;
}
int
my_context_continue(struct my_context *c)
{
DBUG_SWAP_CODE_STATE(&c->dbug_state);
SwitchToFiber(c->lib_fiber);
DBUG_SWAP_CODE_STATE(&c->dbug_state);
return c->return_value;
}
#endif /* MY_CONTEXT_USE_WIN32_FIBERS */
#ifdef MY_CONTEXT_DISABLE
int
my_context_continue(struct my_context *c)
{
return -1;
}
int
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
{
return -1;
}
int
my_context_yield(struct my_context *c)
{
return -1;
}
int
my_context_init(struct my_context *c, size_t stack_size)
{
return -1; /* Out of memory */
}
void
my_context_destroy(struct my_context *c)
{
}
#endif

1997
libmariadb/mysql_async.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,10 @@ global:
mysql_get_host_info; mysql_get_host_info;
mysql_get_proto_info; mysql_get_proto_info;
mysql_get_parameters; mysql_get_parameters;
mysql_get_socket;
mysql_get_server_info; mysql_get_server_info;
mysql_get_timeout_value;
mysql_get_timeout_value_ms;
mysql_get_client_version; mysql_get_client_version;
mysql_get_ssl_cipher; mysql_get_ssl_cipher;
mysql_info; mysql_info;

View File

@@ -24,7 +24,7 @@ ADD_DEFINITIONS(-DLIBMARIADB)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c.in CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c.in
${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c) ${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c)
SET(API_TESTS "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs" SET(API_TESTS "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs"
"sp" "result" "connection" "misc" "ssl" "ps_new" "sqlite3" "thread" "dyncol") "sp" "result" "connection" "misc" "ssl" "ps_new" "sqlite3" "thread" "dyncol")
FOREACH(API_TEST ${API_TESTS}) FOREACH(API_TEST ${API_TESTS})

199
unittest/libmariadb/async.c Normal file
View File

@@ -0,0 +1,199 @@
/*
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 __WIN__
#include <poll.h>
#else
#include <WinSock2.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <mysql.h>
#define SL(s) (s), sizeof(s)
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)
{
/*
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;
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;
res= poll(&pfd, 1, timeout);
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 *my)
{
int err;
MYSQL mysql, *ret;
MYSQL_RES *res;
MYSQL_ROW row;
int status;
mysql_init(&mysql);
mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0);
mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp");
/* 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, NULL,
0, NULL, 0);
while (status)
{
status= wait_for_mysql(&mysql, status);
status= mysql_real_connect_cont(&ret, &mysql, status);
}
FAIL_IF(!ret, "Failed to mysql_real_connect()");
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;
diag("%s: %s", row[0], row[1]);
}
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;
}
struct my_tests_st my_tests[] = {
{"async1", async1, TEST_CONNECTION_DEFAULT, 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());
}