diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..83e2753 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--format documentation +--colour diff --git a/Rakefile b/Rakefile index 6c39ead..7bb616b 100644 --- a/Rakefile +++ b/Rakefile @@ -1,49 +1,5 @@ # encoding: UTF-8 -begin - require 'rubygems' - require 'jeweler' - JEWELER = Jeweler::Tasks.new do |gem| - gem.name = "mysql2" - gem.summary = "A simple, fast Mysql library for Ruby, binding to libmysql" - gem.email = "seniorlopez@gmail.com" - gem.homepage = "http://github.com/brianmario/mysql2" - gem.authors = ["Brian Lopez"] - gem.require_paths = ["lib", "ext"] - gem.extra_rdoc_files = `git ls-files *.rdoc`.split("\n") - gem.files = `git ls-files`.split("\n") - gem.extensions = ["ext/mysql2/extconf.rb"] - gem.files.include %w(lib/jeweler/templates/.document lib/jeweler/templates/.gitignore) - # gem.rubyforge_project = "mysql2" - end -rescue LoadError - puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler -s http://gems.github.com" -end - require 'rake' -require 'spec/rake/spectask' - -desc "Run all examples with RCov" -Spec::Rake::SpecTask.new('spec:rcov') do |t| - t.spec_files = FileList['spec/'] - t.rcov = true - t.rcov_opts = lambda do - IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten - end -end -Spec::Rake::SpecTask.new('spec') do |t| - t.spec_files = FileList['spec/'] - t.spec_opts << '--options' << 'spec/spec.opts' - t.verbose = true - t.warning = true -end - -Spec::Rake::SpecTask.new('spec:gdb') do |t| - t.spec_files = FileList['spec/'] - t.spec_opts << '--options' << 'spec/spec.opts' - t.ruby_cmd = "gdb --args #{RUBY}" -end - -task :default => :spec # Load custom tasks Dir['tasks/*.rake'].sort.each { |f| load f } diff --git a/benchmark/thread_alone.rb b/benchmark/thread_alone.rb deleted file mode 100644 index c6b4c1e..0000000 --- a/benchmark/thread_alone.rb +++ /dev/null @@ -1,20 +0,0 @@ -# encoding: UTF-8 -$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') - -require 'rubygems' -require 'benchmark' -require 'mysql2' - -iterations = 1000 -client = Mysql2::Client.new(:host => "localhost", :username => "root", :database => "test") -query = lambda{ iterations.times{ client.query("SELECT mysql2_test.* FROM mysql2_test") } } -Benchmark.bmbm do |x| - x.report('select') do - query.call - end - x.report('rb_thread_select') do - thread = Thread.new{ sleep(10) } - query.call - thread.kill - end -end \ No newline at end of file diff --git a/ext/mysql2/client.c b/ext/mysql2/client.c index 03d02e5..6079e81 100644 --- a/ext/mysql2/client.c +++ b/ext/mysql2/client.c @@ -104,62 +104,56 @@ static VALUE nogvl_connect(void *ptr) { return client ? Qtrue : Qfalse; } -static void rb_mysql_client_free(void * ptr) { - mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr; - - /* - * we'll send a QUIT message to the server, but that message is more of a - * formality than a hard requirement since the socket is getting shutdown - * anyways, so ensure the socket write does not block our interpreter - */ - int fd = wrapper->client->net.fd; - - if (fd >= 0) { - /* - * if the socket is dead we have no chance of blocking, - * so ignore any potential fcntl errors since they don't matter - */ -#ifndef _WIN32 - int flags = fcntl(fd, F_GETFL); - if (flags > 0 && !(flags & O_NONBLOCK)) - fcntl(fd, F_SETFL, flags | O_NONBLOCK); -#else - u_long iMode = 1; - ioctlsocket(fd, FIONBIO, &iMode); -#endif - } - - /* It's safe to call mysql_close() on an already closed connection. */ - if (!wrapper->closed) { - mysql_close(wrapper->client); - if (!wrapper->freed) { - free(wrapper->client); - } - } - xfree(ptr); -} - -static VALUE nogvl_close(void * ptr) { +static VALUE nogvl_close(void *ptr) { mysql_client_wrapper *wrapper = ptr; if (!wrapper->closed) { wrapper->closed = 1; + + /* + * we'll send a QUIT message to the server, but that message is more of a + * formality than a hard requirement since the socket is getting shutdown + * anyways, so ensure the socket write does not block our interpreter + */ + int fd = wrapper->client->net.fd; + + if (fd >= 0) { + /* + * if the socket is dead we have no chance of blocking, + * so ignore any potential fcntl errors since they don't matter + */ + #ifndef _WIN32 + int flags = fcntl(fd, F_GETFL); + if (flags > 0 && !(flags & O_NONBLOCK)) + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + #else + u_long iMode = 1; + ioctlsocket(fd, FIONBIO, &iMode); + #endif + } + mysql_close(wrapper->client); wrapper->client->net.fd = -1; - if (!wrapper->freed) { - free(wrapper->client); - } + free(wrapper->client); } + return Qnil; } +static void rb_mysql_client_free(void * ptr) { + mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr; + + nogvl_close(wrapper); + + xfree(ptr); +} + static VALUE allocate(VALUE klass) { VALUE obj; mysql_client_wrapper * wrapper; obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper); wrapper->encoding = Qnil; wrapper->active = 0; - wrapper->closed = 0; - wrapper->freed = 0; + wrapper->closed = 1; wrapper->client = (MYSQL*)malloc(sizeof(MYSQL)); return obj; } @@ -523,6 +517,7 @@ static VALUE init_connection(VALUE self) { return rb_raise_mysql2_error(wrapper->client); } + wrapper->closed = 0; return self; } diff --git a/ext/mysql2/client.h b/ext/mysql2/client.h index fca99c4..d5c2993 100644 --- a/ext/mysql2/client.h +++ b/ext/mysql2/client.h @@ -35,7 +35,6 @@ typedef struct { VALUE encoding; short int active; short int closed; - short int freed; MYSQL *client; } mysql_client_wrapper; diff --git a/spec/em/em_spec.rb b/spec/em/em_spec.rb index 1254c4c..429acf1 100644 --- a/spec/em/em_spec.rb +++ b/spec/em/em_spec.rb @@ -1,45 +1,49 @@ # encoding: UTF-8 -require 'spec_helper' -require 'mysql2/em' +if defined? EventMachine + require 'spec_helper' + require 'mysql2/em' -describe Mysql2::EM::Client do - it "should support async queries" do - results = [] - EM.run do - client1 = Mysql2::EM::Client.new - defer1 = client1.query "SELECT sleep(0.1) as first_query" - defer1.callback do |result| - results << result.first - EM.stop_event_loop - end - - client2 = Mysql2::EM::Client.new - defer2 = client2.query "SELECT sleep(0.025) second_query" - defer2.callback do |result| - results << result.first - end - end - - results[0].keys.should include("second_query") - results[1].keys.should include("first_query") - end - - it "should support queries in callbacks" do - results = [] - EM.run do - client = Mysql2::EM::Client.new - defer1 = client.query "SELECT sleep(0.025) as first_query" - defer1.callback do |result| - results << result.first - defer2 = client.query "SELECT sleep(0.025) as second_query" - defer2.callback do |result| + describe Mysql2::EM::Client do + it "should support async queries" do + results = [] + EM.run do + client1 = Mysql2::EM::Client.new + defer1 = client1.query "SELECT sleep(0.1) as first_query" + defer1.callback do |result| results << result.first EM.stop_event_loop end + + client2 = Mysql2::EM::Client.new + defer2 = client2.query "SELECT sleep(0.025) second_query" + defer2.callback do |result| + results << result.first + end end + + results[0].keys.should include("second_query") + results[1].keys.should include("first_query") end - results[0].keys.should include("first_query") - results[1].keys.should include("second_query") + it "should support queries in callbacks" do + results = [] + EM.run do + client = Mysql2::EM::Client.new + defer1 = client.query "SELECT sleep(0.025) as first_query" + defer1.callback do |result| + results << result.first + defer2 = client.query "SELECT sleep(0.025) as second_query" + defer2.callback do |result| + results << result.first + EM.stop_event_loop + end + end + end + + results[0].keys.should include("first_query") + results[1].keys.should include("second_query") + end end -end +else + puts "EventMachine not installed, skipping the specs that use it" +end \ No newline at end of file diff --git a/spec/spec.opts b/spec/spec.opts deleted file mode 100644 index e0f74bb..0000000 --- a/spec/spec.opts +++ /dev/null @@ -1,2 +0,0 @@ ---format specdoc ---colour diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 39da1e4..a0e5770 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,10 +1,11 @@ # encoding: UTF-8 -require 'rubygems' +$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) +require 'rspec' require 'mysql2' require 'timeout' -Spec::Runner.configure do |config| +RSpec.configure do |config| config.before(:all) do client = Mysql2::Client.new :database => 'test' client.query %[ diff --git a/tasks/jeweler.rake b/tasks/jeweler.rake new file mode 100644 index 0000000..e04979c --- /dev/null +++ b/tasks/jeweler.rake @@ -0,0 +1,17 @@ +begin + require 'jeweler' + JEWELER = Jeweler::Tasks.new do |gem| + gem.name = "mysql2" + gem.summary = "A simple, fast Mysql library for Ruby, binding to libmysql" + gem.email = "seniorlopez@gmail.com" + gem.homepage = "http://github.com/brianmario/mysql2" + gem.authors = ["Brian Lopez"] + gem.require_paths = ["lib", "ext"] + gem.extra_rdoc_files = `git ls-files *.rdoc`.split("\n") + gem.files = `git ls-files`.split("\n") + gem.extensions = ["ext/mysql2/extconf.rb"] + gem.files.include %w(lib/jeweler/templates/.document lib/jeweler/templates/.gitignore) + end +rescue LoadError + puts "jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler" +end \ No newline at end of file diff --git a/tasks/rspec.rake b/tasks/rspec.rake new file mode 100644 index 0000000..a6628e8 --- /dev/null +++ b/tasks/rspec.rake @@ -0,0 +1,16 @@ +begin + require 'rspec' + require 'rspec/core/rake_task' + + desc "Run all examples with RCov" + RSpec::Core::RakeTask.new('spec:rcov') do |t| + t.rcov = true + end + RSpec::Core::RakeTask.new('spec') do |t| + t.verbose = true + end + + task :default => :spec +rescue LoadError + puts "rspec, or one of its dependencies, is not available. Install it with: sudo gem install rspec" +end \ No newline at end of file