You've already forked mariadb-connector-c
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:
@@ -20,6 +20,11 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* unsupported macros (used by async) */
|
||||
#define DBUG_SWAP_CODE_STATE(a) {}
|
||||
#define DBUG_FREE_CODE_STATE(a) {}
|
||||
|
||||
#if !defined(DBUG_OFF) && !defined(_lint)
|
||||
|
||||
struct _db_stack_frame_ {
|
||||
@@ -120,7 +125,6 @@ extern const char* _db_get_func_(void);
|
||||
#endif
|
||||
|
||||
#else /* No debugger */
|
||||
|
||||
#define DBUG_ENTER(a1)
|
||||
#define DBUG_END() {}
|
||||
#define DBUG_RETURN(a1) return(a1)
|
||||
|
@@ -31,12 +31,15 @@ typedef struct st_mariadb_db_driver
|
||||
void *buffer;
|
||||
} MARIADB_DB_DRIVER;
|
||||
|
||||
struct st_mysql_options_extention {
|
||||
struct mysql_async_context;
|
||||
|
||||
struct st_mysql_options_extension {
|
||||
char *plugin_dir;
|
||||
char *default_auth;
|
||||
char *ssl_crl;
|
||||
char *ssl_crlpath;
|
||||
char *server_public_key_path;
|
||||
struct mysql_async_context *async_context;
|
||||
HASH connect_attrs;
|
||||
size_t connect_attrs_len;
|
||||
void (*report_progress)(const MYSQL *mysql,
|
||||
|
232
include/my_context.h
Normal file
232
include/my_context.h
Normal 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;
|
||||
};
|
@@ -364,13 +364,14 @@ typedef int (*qsort_cmp)(const void *,const void *);
|
||||
#else
|
||||
#define qsort_t RETQSORTTYPE /* Broken GCC cant handle typedef !!!! */
|
||||
#endif
|
||||
#ifdef HAVE_mit_thread
|
||||
#define size_socket socklen_t /* Type of last arg to accept */
|
||||
#else
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
typedef SOCKET_SIZE_TYPE size_socket;
|
||||
|
||||
#ifndef SOCKOPT_OPTLEN_TYPE
|
||||
#define SOCKOPT_OPTLEN_TYPE size_socket
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifndef SOCKOPT_OPTLEN_TYPE
|
||||
@@ -1085,6 +1086,22 @@ do { doubleget_union _tmp; \
|
||||
#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
|
||||
#define RTLD_NOW 1
|
||||
#endif
|
||||
|
@@ -144,6 +144,10 @@ typedef long longlong;
|
||||
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) \
|
||||
{ \
|
||||
(a)->net.last_errno= 0;\
|
||||
@@ -210,6 +214,7 @@ typedef long longlong;
|
||||
|
||||
/* MariaDB specific */
|
||||
MYSQL_PROGRESS_CALLBACK=5999,
|
||||
MYSQL_OPT_NONBLOCK,
|
||||
MYSQL_DATABASE_DRIVER=7000
|
||||
};
|
||||
|
||||
@@ -229,7 +234,7 @@ typedef long longlong;
|
||||
MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY
|
||||
};
|
||||
|
||||
struct st_mysql_options_extention;
|
||||
struct st_mysql_options_extension;
|
||||
|
||||
struct st_mysql_options {
|
||||
unsigned int connect_timeout, read_timeout, write_timeout;
|
||||
@@ -258,7 +263,7 @@ struct st_mysql_options {
|
||||
void (*local_infile_end)(void *);
|
||||
int (*local_infile_error)(void *, char *, unsigned int);
|
||||
void *local_infile_userdata;
|
||||
struct st_mysql_options_extention *extension;
|
||||
struct st_mysql_options_extension *extension;
|
||||
};
|
||||
|
||||
typedef struct st_mysql {
|
||||
@@ -330,6 +335,12 @@ typedef struct st_mysql_time
|
||||
#define AUTO_SEC_PART_DIGITS 31
|
||||
#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
|
||||
{
|
||||
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, ...);
|
||||
MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void);
|
||||
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>
|
||||
|
||||
|
38
include/mysql_async.h
Normal file
38
include/mysql_async.h
Normal 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 */
|
@@ -35,6 +35,13 @@
|
||||
#include <openssl/ssl.h>
|
||||
#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 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -138,6 +145,8 @@ struct st_vio
|
||||
int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */
|
||||
struct sockaddr_in local; /* Local 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 */
|
||||
char desc[30]; /* String description */
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
@@ -53,6 +53,7 @@ mf_wcomp.c
|
||||
mulalloc.c
|
||||
my_alloc.c
|
||||
my_compress.c
|
||||
my_context.c
|
||||
my_div.c
|
||||
my_error.c
|
||||
my_fopen.c
|
||||
@@ -74,6 +75,7 @@ my_static.c
|
||||
my_symlink.c
|
||||
my_thr_init.c
|
||||
my_write.c
|
||||
mysql_async.c
|
||||
password.c
|
||||
str2int.c
|
||||
strcend.c
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <m_string.h>
|
||||
#include <m_ctype.h>
|
||||
#include <ma_common.h>
|
||||
#include "my_context.h"
|
||||
#include "mysql.h"
|
||||
#include "mysql_version.h"
|
||||
#include "mysqld_error.h"
|
||||
@@ -69,6 +70,8 @@
|
||||
#endif
|
||||
#include <ma_dyncol.h>
|
||||
|
||||
#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15)
|
||||
|
||||
#undef max_allowed_packet
|
||||
#undef net_buffer_length
|
||||
extern ulong max_allowed_packet; /* net.c */
|
||||
@@ -119,7 +122,6 @@ struct st_mysql_methods MARIADB_DEFAULT_METHODS;
|
||||
#include <mysql/client_plugin.h>
|
||||
|
||||
#define native_password_plugin_name "mysql_native_password"
|
||||
const char *unknown_sqlstate= "HY000";
|
||||
|
||||
static void end_server(MYSQL *mysql);
|
||||
static void mysql_close_memory(MYSQL *mysql);
|
||||
@@ -841,8 +843,8 @@ enum option_val
|
||||
|
||||
#define CHECK_OPT_EXTENSION_SET(OPTS)\
|
||||
if (!(OPTS)->extension) \
|
||||
(OPTS)->extension= (struct st_mysql_options_extention *) \
|
||||
my_malloc(sizeof(struct st_mysql_options_extention), \
|
||||
(OPTS)->extension= (struct st_mysql_options_extension *) \
|
||||
my_malloc(sizeof(struct st_mysql_options_extension), \
|
||||
MYF(MY_WME | MY_ZEROFILL)); \
|
||||
|
||||
#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),
|
||||
mysql->options.connect_timeout) <0)
|
||||
{
|
||||
printf("err\n");
|
||||
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),
|
||||
unix_socket, socket_errno);
|
||||
@@ -2188,7 +2191,7 @@ void my_set_error(MYSQL *mysql,
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void mthd_my_close(MYSQL *mysql)
|
||||
void STDCALL mysql_close_slow_part(MYSQL *mysql)
|
||||
{
|
||||
if (mysql->net.vio)
|
||||
{
|
||||
@@ -2813,7 +2816,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
|
||||
{
|
||||
va_list ap;
|
||||
void *arg1;
|
||||
|
||||
struct mysql_async_context *ctxt;
|
||||
size_t stacksize;
|
||||
|
||||
DBUG_ENTER("mysql_option");
|
||||
DBUG_PRINT("enter",("option: %d",(int) option));
|
||||
@@ -2881,8 +2885,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
|
||||
break;
|
||||
case MYSQL_PROGRESS_CALLBACK:
|
||||
if (!mysql->options.extension)
|
||||
mysql->options.extension= (struct st_mysql_options_extention *)
|
||||
my_malloc(sizeof(struct st_mysql_options_extention),
|
||||
mysql->options.extension= (struct st_mysql_options_extension *)
|
||||
my_malloc(sizeof(struct st_mysql_options_extension),
|
||||
MYF(MY_WME | MY_ZEROFILL));
|
||||
if (mysql->options.extension)
|
||||
mysql->options.extension->report_progress=
|
||||
@@ -2901,8 +2905,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
|
||||
MYSQL_CLIENT_DB_PLUGIN)))
|
||||
break;
|
||||
if (!mysql->options.extension)
|
||||
mysql->options.extension= (struct st_mysql_options_extention *)
|
||||
my_malloc(sizeof(struct st_mysql_options_extention),
|
||||
mysql->options.extension= (struct st_mysql_options_extension *)
|
||||
my_malloc(sizeof(struct st_mysql_options_extension),
|
||||
MYF(MY_WME | MY_ZEROFILL));
|
||||
if (!mysql->options.extension->db_driver)
|
||||
mysql->options.extension->db_driver= (MARIADB_DB_DRIVER *)
|
||||
@@ -2917,6 +2921,48 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
|
||||
}
|
||||
}
|
||||
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:
|
||||
if (*(uint *)arg1)
|
||||
mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT;
|
||||
@@ -3431,6 +3477,14 @@ mysql_get_parameters(void)
|
||||
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
|
||||
* stored in mysql->methods and can be overwritten by
|
||||
@@ -3440,7 +3494,7 @@ struct st_mysql_methods MARIADB_DEFAULT_METHODS = {
|
||||
/* open a connection */
|
||||
mthd_my_real_connect,
|
||||
/* close connection */
|
||||
mthd_my_close,
|
||||
mysql_close_slow_part,
|
||||
/* send command to server */
|
||||
mthd_my_send_cmd,
|
||||
/* skip result set */
|
||||
|
@@ -39,6 +39,9 @@ EXPORTS
|
||||
mysql_get_server_info
|
||||
mysql_get_client_version
|
||||
mysql_get_ssl_cipher
|
||||
mysql_get_socket
|
||||
mysql_get_timeout_value
|
||||
mysql_get_timeout_value_ms
|
||||
mysql_info
|
||||
mysql_init
|
||||
mysql_insert_id
|
||||
|
759
libmariadb/my_context.c
Normal file
759
libmariadb/my_context.c
Normal 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
1997
libmariadb/mysql_async.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,10 @@ global:
|
||||
mysql_get_host_info;
|
||||
mysql_get_proto_info;
|
||||
mysql_get_parameters;
|
||||
mysql_get_socket;
|
||||
mysql_get_server_info;
|
||||
mysql_get_timeout_value;
|
||||
mysql_get_timeout_value_ms;
|
||||
mysql_get_client_version;
|
||||
mysql_get_ssl_cipher;
|
||||
mysql_info;
|
||||
|
@@ -24,7 +24,7 @@ ADD_DEFINITIONS(-DLIBMARIADB)
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c.in
|
||||
${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")
|
||||
|
||||
FOREACH(API_TEST ${API_TESTS})
|
||||
|
199
unittest/libmariadb/async.c
Normal file
199
unittest/libmariadb/async.c
Normal 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());
|
||||
}
|
Reference in New Issue
Block a user