diff --git a/ext/extconf.rb b/ext/extconf.rb index e5908de..b200c2c 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -45,8 +45,10 @@ else exit 1 end +# check for 1.9 if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h') - $CPPFLAGS << " -DHAVE_TBR" + $CFLAGS += " -DHAVE_TBR " + $CPPFLAGS << " -DHAVE_TBR " end # make mysql constant diff --git a/ext/mysql.c b/ext/mysql.c index c6c5b86..fc22491 100644 --- a/ext/mysql.c +++ b/ext/mysql.c @@ -6,6 +6,7 @@ #include #include +#include #ifndef RSTRING_PTR #define RSTRING_PTR(str) RSTRING(str)->ptr #endif @@ -233,8 +234,71 @@ static VALUE init(VALUE klass) return obj; } +#ifdef HAVE_TBR + +typedef struct +{ + void *func_pointer; + int param_count; + void *args[10]; +} arg_holder, *arg_holder2; + + +static void call_single_function_rb_thread_blocking_region(void *arg_holder_in); + +void *rb_thread_blocking_region_variable_params(int number, ...) +{ + va_list param_pt; // TODO handle all the way through 10 args + va_start(param_pt, number); + int index; + arg_holder param_storer; + void *func_pointer = va_arg(param_pt, void *); + void *interrupter = va_arg(param_pt, void *); + param_storer.func_pointer = func_pointer; + int real_param_count = number - 2; + param_storer.param_count = real_param_count; + for(index = 0 ; index < real_param_count ; index++) + { + void *arg = va_arg(param_pt, void *); + param_storer.args[index] = arg; + + } + va_end(param_pt); + + return rb_thread_blocking_region((rb_blocking_function_t *)call_single_function_rb_thread_blocking_region, (void *) ¶m_storer, interrupter, 0); + +} + +static void call_single_function_rb_thread_blocking_region(void *arg_holder_in) +{ + arg_holder *params_and_func = (arg_holder *) arg_holder_in; + int param_count = params_and_func->param_count; + void *result; + if(param_count == 3) + { + void * (*pt2Func)(void *, void *, void *) = params_and_func->func_pointer; + result = (*pt2Func)(params_and_func->args[0], params_and_func->args[1], params_and_func->args[2]); + }else if(param_count == 6) + { + void * (*pt2Func)(void *, void *, void *, void *, void *, void *) = params_and_func->func_pointer; + result = (*pt2Func)(params_and_func->args[0], params_and_func->args[1], params_and_func->args[2], params_and_func->args[3], params_and_func->args[4], params_and_func->args[5]); + }else if(param_count == 8) + { + void * (*pt2Func)(void *, void *, void *, void *, void *, void *, void *, void *) = params_and_func->func_pointer; + result = (*pt2Func)(params_and_func->args[0], params_and_func->args[1], params_and_func->args[2], params_and_func->args[3], params_and_func->args[4], params_and_func->args[5], params_and_func->args[6], params_and_func->args[7]); + }else + { + printf("UN nonwn param count--please add it! %d\n", param_count); + result = Qnil; + } + + return result; +} + +#endif + /* real_connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, sock=nil, flag=nil) */ -static VALUE real_connect(int argc, VALUE* argv, VALUE klass) +static VALUE real_connect(int argc, VALUE* argv, VALUE klass) /* actually gets run */ { VALUE host, user, passwd, db, port, sock, flag; char *h, *u, *p, *d, *s; @@ -260,8 +324,12 @@ static VALUE real_connect(int argc, VALUE* argv, VALUE klass) obj = Data_Make_Struct(klass, struct mysql, 0, free_mysql, myp); #if MYSQL_VERSION_ID >= 32200 - mysql_init(&myp->handler); - if (mysql_real_connect(&myp->handler, h, u, p, d, pp, s, f) == NULL) + mysql_init(&myp->handler); /* we get here */ +# ifdef HAVE_TBR + if( (int) rb_thread_blocking_region_variable_params(10, &mysql_real_connect, 8, &myp->handler, h, u, p, d, pp, s, f) == NULL) +# else + if(mysql_real_connect(&myp->handler, h, u, p, d, pp, s, f) == NULL) +# endif #elif MYSQL_VERSION_ID >= 32115 if (mysql_real_connect(&myp->handler, h, u, p, pp, s, f) == NULL) #else @@ -694,13 +762,38 @@ static VALUE my_stat(VALUE obj) return rb_tainted_str_new2(s); } +typedef struct +{ + MYSQL *mysql_instance; + MYSQL_RES **store_it_here; + +} mysql_result_to_here_t, + *shared_stuff_p; + +static VALUE store_result_to_location(void *settings_in) +{ + mysql_result_to_here_t *settings = (mysql_result_to_here_t *) settings_in; + *(settings->store_it_here) = mysql_store_result(settings->mysql_instance); // this one runs a good long while for very large queries + return Qnil; +} + /* store_result() */ static VALUE store_result(VALUE obj) { MYSQL* m = GetHandler(obj); - MYSQL_RES* res = mysql_store_result(m); + MYSQL_RES* res = NULL; +#ifndef HAVE_TBR + res = mysql_store_result(m); +#else + mysql_result_to_here_t linker; + linker.mysql_instance = m; + linker.store_it_here = &res; + rb_thread_blocking_region(store_result_to_location, (void *) &linker, RUBY_UBF_IO, 0); /* not sure if this should be RUBY_UBF_IO or RUBY_UBF_PROCESS here -- see Ruby 1.9 ChangeLog */ +#endif + if (res == NULL) mysql_raise(m); + return mysqlres2obj(res); } @@ -800,12 +893,17 @@ static VALUE socket(VALUE obj) MYSQL* m = GetHandler(obj); return INT2NUM(m->net.fd); } -/* socket_type */ +/* socket_type --currently returns true or false, needs some work */ static VALUE socket_type(VALUE obj) { MYSQL* m = GetHandler(obj); + char *answer; VALUE description = vio_description( m->net.vio ); - return (VALUE) NILorSTRING( description ); + answer = NILorSTRING( description ); + if(answer) + return Qtrue; // TODO return a ruby string + else + return Qnil; } /* blocking */ diff --git a/test/test_all_hashes.rb b/test/test_all_hashes.rb index 3581c04..0f839b0 100644 --- a/test/test_all_hashes.rb +++ b/test/test_all_hashes.rb @@ -1,7 +1,8 @@ # shows the effect of using .all_hashes instead of looping on each hash # run it by substiting in a 'long' [many row] query for the query variable and toggling use_all_hashes here at the top # note that we load all the rows first, then run .all_hashes on the result [to see more easily the effect of all hashes] -# on my machine and a 200_000 row table, it took 3.38s versus 3.65s +# on my machine and a 200_000 row table, it took 3.38s versus 3.65s for the old .each_hash way [note also that .each_hash is +# almost as fast, now, as .all_hashes--they've both been optimized] require 'rubygems' require 'mysqlplus'