Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions contrib/ruby/lib/trilogy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,27 @@
require "trilogy/encoding"

class Trilogy
Synchronization = Module.new

source = public_instance_methods(false).map do |method|
<<~RUBY
def #{method}(...)
raise SynchronizationError unless @mutex.try_lock

begin
super
ensure
@mutex.unlock
end
end
RUBY
end
Synchronization.class_eval(source.join(";"))

prepend(Synchronization)

def initialize(options = {})
@mutex = Mutex.new
options[:port] = options[:port].to_i if options[:port]
mysql_encoding = options[:encoding] || "utf8mb4"
encoding = Trilogy::Encoding.find(mysql_encoding)
Expand Down
6 changes: 6 additions & 0 deletions contrib/ruby/lib/trilogy/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ class BaseConnectionError < BaseError
include ConnectionError
end

class SynchronizationError < BaseError
def initialize(message = "This connection is already in use by another thread or fiber")
super
end
end

# Trilogy::ClientError is the base error type for invalid queries or parameters
# that shouldn't be retried.
class ClientError < BaseError
Expand Down
11 changes: 11 additions & 0 deletions contrib/ruby/test/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,17 @@ def test_releases_gvl
end
end

def test_prevent_concurrent_use
client = new_tcp_client
thread = Thread.new { client.query("SELECT SLEEP(1)") }
thread.join(0.2)
assert_raises Trilogy::SynchronizationError do
client.query("SELECT 1")
end
thread.join
client.close
end

USR1 = Class.new(StandardError)

def test_interruptible_when_releasing_gvl
Expand Down