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
|
#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)
|
||||||
|
@@ -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
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
|
#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
|
||||||
|
@@ -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
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>
|
#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
|
||||||
|
@@ -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
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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
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_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;
|
||||||
|
@@ -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
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