From a5b881594da4258257b18cc42f5ce7be3524e02c Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 20 Sep 2011 12:49:25 +0200 Subject: MWL#192: Non-blocking client API for libmysqlclient. All client functions that can block on I/O have alternate _start() and _cont() versions that do not block but return control back to the application, which can then issue I/O wait in its own fashion and later call back into the library to continue the operation. Works behind the scenes by spawning a co-routine/fiber to run the blocking operation and suspend it while waiting for I/O. This co-routine/fiber use is invisible to applications. For i368/x86_64 on GCC, uses very fast assembler co-routine support. On Windows uses native Win32 Fibers. Falls back to POSIX ucontext on other platforms. Assembler routines for more platforms are relatively easy to add by extending mysys/my_context.c, eg. similar to the Lua lcoco library. For testing, mysqltest and mysql_client_test are extended with the option --non-blocking-api. This causes the programs to use the non-blocking API for database access. mysql-test-run.pl has a similar option --non-blocking-api that uses this, as well as additional testcases. An example program tests/async_queries.c is included that uses the new non-blocking API with libevent to show how, in a single-threaded program, to issue many queries in parallel against a database. client/async_example.c: Fix const warning ****** Fix bug with wrong timeout value for poll(). include/Makefile.am: Fix missing include for `make dist` include/mysql.h: Add prototypes for all non-blocking API calls. include/mysql.h.pp: Add prototypes for all non-blocking API calls. mysys/my_context.c: Fix type warning for makecontext() function pointer argument. sql-common/mysql_async.c: Fix crashes in the non-blocking API for functions that can take MYSQL argument that is NULL. tests/Makefile.am: Add header file to `make dist` tests/mysql_client_test.c: Replace blocking calls with wrappers around the non-blocking calls, used in mysql_client_test to test the new non-blocking API. tests/nonblock-wrappers.h: Replace blocking calls with wrappers around the non-blocking calls, used in mysql_client_test to test the new non-blocking API. --- tests/check_async_queries.pl | 73 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 tests/check_async_queries.pl (limited to 'tests/check_async_queries.pl') diff --git a/tests/check_async_queries.pl b/tests/check_async_queries.pl new file mode 100644 index 00000000000..b599bc334d3 --- /dev/null +++ b/tests/check_async_queries.pl @@ -0,0 +1,73 @@ +#! /usr/bin/perl + +# Read the output of async_queries.c. Run the queries again serially, using +# the normal (not asynchronous) API. Compare the two results for correctness. + +use strict; +use warnings; + +use DBI; + +my $D= []; + +die "Usage: $0 \n" + unless @ARGV == 4; + +my $dbh= DBI->connect("DBI:mysql:database=$ARGV[3];host=$ARGV[0]", + $ARGV[1], $ARGV[2], + { RaiseError => 1, PrintError => 0 }); + +while () { + chomp; + if (/^([0-9]+) ! (.*);$/) { + my ($index, $query)= ($1, $2); + $D->[$index]= { QUERY => $query, OUTPUT => [] }; + } elsif (/^([0-9]+) - (.*)$/) { + my ($index, $data)= ($1, $2); + push @{$D->[$index]{OUTPUT}}, $data; + } elsif (/^([0-9]+) \| Error: (.*)$/) { + my ($index, $errmsg)= ($1, $2); + my $rows; + my $res= eval { + my $stm= $dbh->prepare($D->[$index]{QUERY}); + $stm->execute(); + $rows= $stm->fetchall_arrayref(); + 1; + }; + if ($res) { + die "Query $index succeeded, but should have failed with error.\nquery=$D->[$index]{QUERY}\nerror=$errmsg\n"; + } + my $errmsg2= $@; + if ($errmsg2 =~ /^DBD::.*failed: (.*) at .*$/s) { + $errmsg2= $1; + } else { + die "Unexpected DBD error message format: '$errmsg2'\n"; + } + if ($errmsg2 ne $errmsg) { + die "Query $index failed with different error message\nquery=$D->[$index]{QUERY}\nerror1=$errmsg\nerror2=$errmsg2\n"; + } + print "OK $index\n"; + delete $D->[$index]; + } elsif (/^([0-9]+) \| EOF$/) { + my $index= $1; + my $rows; + my $res= eval { + my $stm= $dbh->prepare($D->[$index]{QUERY}); + $stm->execute(); + $rows= $stm->fetchall_arrayref(); + 1; + }; + if (!$res) { + die "Query $index failed, but should have succeeded.\nquery=$D->[$index]{QUERY}\nerror=$@\n"; + } + my $result_string= join("\n", sort @{$D->[$index]{OUTPUT}}); + my $result_string2= join("\n", sort(map(join("\t", map((defined($_) ? $_ : "(null)"), @$_)), @$rows))); + if ($result_string ne $result_string2) { + die "Query $index result difference.\nquery=$D->[$index]{QUERY}\noutput1=\n$$result_string\noutput2=\n$result_string2\n"; + } + delete $D->[$index]; + } else { + die "Unexpected line: '$_'\n"; + } +} +$dbh->disconnect(); -- cgit v1.2.1