From 13b96a559a71bfa2ebeb6a35ef36a21bd0b1f981 Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Fri, 3 Aug 2012 10:02:12 +0200 Subject: [PATCH 01/10] add dependency for semaphore gem --- Gemfile.lock | 4 +++- officer.gemspec | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index ed64b88..2264760 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,6 +6,7 @@ PATH daemons eventmachine json + semaphore (>= 0.0.1) GEM remote: https://rubygems.org/ @@ -14,7 +15,7 @@ GEM daemons (1.1.8) diff-lcs (1.1.3) eventmachine (0.12.10) - json (1.7.3) + json (1.7.4) rake (0.9.2.2) rspec (2.10.0) rspec-core (~> 2.10.0) @@ -24,6 +25,7 @@ GEM rspec-expectations (2.10.0) diff-lcs (~> 1.1.3) rspec-mocks (2.10.1) + semaphore (0.0.1) PLATFORMS ruby diff --git a/officer.gemspec b/officer.gemspec index 78fd118..a63829a 100644 --- a/officer.gemspec +++ b/officer.gemspec @@ -19,6 +19,7 @@ Gem::Specification.new do |gem| gem.add_dependency('json', ['>= 0']) gem.add_dependency('daemons', ['>= 0']) gem.add_dependency('choice', ['>= 0']) + gem.add_dependency('semaphore', ['>= 0.0.1']) gem.add_development_dependency('rake', ['>= 0']) gem.add_development_dependency('rspec', ['>= 0']) From ca39009a72df53fd5057e2bfadf3c588ae17c3aa Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Mon, 6 Aug 2012 08:04:38 +0200 Subject: [PATCH 02/10] add size to lock_store --- lib/officer/lock_store.rb | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/officer/lock_store.rb b/lib/officer/lock_store.rb index 779da80..ef2d8d5 100644 --- a/lib/officer/lock_store.rb +++ b/lib/officer/lock_store.rb @@ -8,10 +8,12 @@ def to_host_a class Lock attr_reader :name + attr_reader :size attr_reader :queue - def initialize name - @name = name + def initialize name, size = 1 + @name = "#{name}_#{size}" + @size = size.to_i @queue = LockQueue.new end end @@ -52,6 +54,10 @@ def log_state end def acquire name, connection, options={} + name, size = split_name(name) + + lockname = "#{name}_#{size}" + if options[:queue_max] lock = @locks[name] @@ -63,20 +69,22 @@ def acquire name, connection, options={} @acquire_counter += 1 - lock = @locks[name] ||= Lock.new(name) + lock = @locks[name] ||= Lock.new(name, size) if lock.queue.include? connection - lock.queue.first == connection ? connection.already_acquired(name) : connection.queued(name, options) + lock.queue[0..lock.size-1].include?(connection) ? connection.already_acquired(name) : connection.queued(name, options) else lock.queue << connection (@connections[connection] ||= Set.new) << name - lock.queue.count == 1 ? connection.acquired(name) : connection.queued(name, options) + lock.queue.count <= lock.size ? connection.acquired(name) : connection.queued(name, options) end end def release name, connection, options={} + name, size = split_name(name) + if options[:callback].nil? options[:callback] = true end @@ -93,16 +101,15 @@ def release name, connection, options={} # If connecton has the lock, release it and let the next # connection know that it has acquired the lock. - if lock.queue.first == connection - lock.queue.shift + if lock.queue[0..lock.size-1].delete(connection) connection.released name if options[:callback] - if next_connection = lock.queue.first + if next_connection = lock.queue[lock.size-1] next_connection.acquired name - else - @locks.delete name end + @locks.delete if lock.queue.count = 0 + # If the connection is queued and doesn't have the lock, # dequeue it and leave the other connections alone. else @@ -125,6 +132,8 @@ def reset connection end def timeout name, connection + name, size = split_name(name) + lock = @locks[name] names = @connections[connection] @@ -169,6 +178,15 @@ def close_idle_connections(max_idle) end end end + + protected + def split_name(name) + name_array = name.split("#") + size = name_array.pop || 1 + name = name_array.join("#") + + [name, size] + end end end From 83ef4e6862e1523734b00ed70887cbf1cb69c25e Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Mon, 6 Aug 2012 11:16:15 +0200 Subject: [PATCH 03/10] remove semaphore dependency --- Gemfile.lock | 16 +++++++--------- officer.gemspec | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2264760..fdd6fe4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,7 +6,6 @@ PATH daemons eventmachine json - semaphore (>= 0.0.1) GEM remote: https://rubygems.org/ @@ -17,15 +16,14 @@ GEM eventmachine (0.12.10) json (1.7.4) rake (0.9.2.2) - rspec (2.10.0) - rspec-core (~> 2.10.0) - rspec-expectations (~> 2.10.0) - rspec-mocks (~> 2.10.0) - rspec-core (2.10.1) - rspec-expectations (2.10.0) + rspec (2.11.0) + rspec-core (~> 2.11.0) + rspec-expectations (~> 2.11.0) + rspec-mocks (~> 2.11.0) + rspec-core (2.11.1) + rspec-expectations (2.11.2) diff-lcs (~> 1.1.3) - rspec-mocks (2.10.1) - semaphore (0.0.1) + rspec-mocks (2.11.1) PLATFORMS ruby diff --git a/officer.gemspec b/officer.gemspec index a63829a..3b43b5a 100644 --- a/officer.gemspec +++ b/officer.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |gem| gem.add_dependency('json', ['>= 0']) gem.add_dependency('daemons', ['>= 0']) gem.add_dependency('choice', ['>= 0']) - gem.add_dependency('semaphore', ['>= 0.0.1']) + #gem.add_dependency('semaphore', ['>= 0.0.1']) gem.add_development_dependency('rake', ['>= 0']) gem.add_development_dependency('rspec', ['>= 0']) From 2fca2f1ef536eb993c0a231090d89f5b7f9370ca Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Wed, 15 Aug 2012 11:24:41 +0200 Subject: [PATCH 04/10] add lock_id to semaphore --- README.markdown | 6 +++ lib/officer/connection.rb | 4 +- lib/officer/lock_store.rb | 81 ++++++++++++++++++++------------ officer.gemspec | 1 - spec/integration/officer_spec.rb | 66 +++++++++++++++++++++++++- 5 files changed, 122 insertions(+), 36 deletions(-) diff --git a/README.markdown b/README.markdown index 60abcf4..3368b8a 100644 --- a/README.markdown +++ b/README.markdown @@ -69,6 +69,12 @@ Options: - :timeout => The number of seconds to wait for a lock to become available (default: wait forever). - :queue_max => If the lock queue length is greater than :queue_max then don't wait for the lock (default: infinite). +#### How to use n Semaphore + +To allow multiple clients to use the same lock you can use n Semaphore functionality. +Officer can use a n Semaphore in stead of a Mutex by adding #n to the lock name. + + client.lock 'some_lock_name#2' # will create a 2 semaphore ### Unlock diff --git a/lib/officer/connection.rb b/lib/officer/connection.rb index ac2eabe..da464b7 100644 --- a/lib/officer/connection.rb +++ b/lib/officer/connection.rb @@ -34,10 +34,10 @@ def unbind end module LockStoreCallbacks - def acquired name + def acquired name, lock_id @timers.delete(name).cancel if @timers[name] - send_result 'acquired', :name => name + send_result 'acquired', :name => name, :lock_id => lock_id.to_s end def already_acquired name diff --git a/lib/officer/lock_store.rb b/lib/officer/lock_store.rb index ef2d8d5..ed1e25a 100644 --- a/lib/officer/lock_store.rb +++ b/lib/officer/lock_store.rb @@ -10,10 +10,12 @@ class Lock attr_reader :name attr_reader :size attr_reader :queue + attr_reader :lock_ids def initialize name, size = 1 - @name = "#{name}_#{size}" + @name = name @size = size.to_i + @lock_ids = (0..@size-1).to_a @queue = LockQueue.new end end @@ -24,6 +26,7 @@ class LockStore def initialize @locks = {} # name => Lock @connections = {} # Connection => Set(name, ...) + @locked_connections = {} # {lock_id => Connection} @acquire_counter = 0 end @@ -56,8 +59,6 @@ def log_state def acquire name, connection, options={} name, size = split_name(name) - lockname = "#{name}_#{size}" - if options[:queue_max] lock = @locks[name] @@ -70,15 +71,22 @@ def acquire name, connection, options={} @acquire_counter += 1 lock = @locks[name] ||= Lock.new(name, size) - - if lock.queue.include? connection - lock.queue[0..lock.size-1].include?(connection) ? connection.already_acquired(name) : connection.queued(name, options) - - else + puts lock.queue.inspect + if lock.queue.count < lock.size + if lock.queue[0..lock.size-1].include?(connection) + connection.already_acquired(name) + else + lock.queue << connection + lock_id = lock_connection(connection, lock) + (@connections[connection] ||= Set.new) << name + + connection.acquired name, lock_id + end + elsif lock.queue.count >= lock.size lock.queue << connection (@connections[connection] ||= Set.new) << name - lock.queue.count <= lock.size ? connection.acquired(name) : connection.queued(name, options) + connection.queued(name, options) end end @@ -88,39 +96,41 @@ def release name, connection, options={} if options[:callback].nil? options[:callback] = true end - lock = @locks[name] names = @connections[connection] - # Client should only be able to release a lock that # exists and that it has previously queued. - if lock.nil? || !names.include?(name) + if lock.nil? || names.nil? || !names.include?(name) connection.release_failed(name) if options[:callback] return end # If connecton has the lock, release it and let the next # connection know that it has acquired the lock. - if lock.queue[0..lock.size-1].delete(connection) - connection.released name if options[:callback] - - if next_connection = lock.queue[lock.size-1] - next_connection.acquired name + if index = lock.queue.index(connection) + if index < lock.size + lock.queue.delete_at(index) + + connection.released name if options[:callback] + + if next_connection = lock.queue[lock.size-1] + lock_id = lock_connection(next_connection, lock) + next_connection.acquired name, lock_id + end + @locks.delete name if lock.queue.count == 0 + + # If the connection is queued and doesn't have the lock, + # dequeue it and leave the other connections alone. + else + lock.queue.delete connection + connection.released name end - - @locks.delete if lock.queue.count = 0 - - # If the connection is queued and doesn't have the lock, - # dequeue it and leave the other connections alone. - else - lock.queue.delete connection - connection.released name + names.delete name end - - names.delete name end def reset connection + # names = @connections[connection] || [] names = @connections[connection] || Set.new names.each do |name| @@ -181,12 +191,21 @@ def close_idle_connections(max_idle) protected def split_name(name) - name_array = name.split("#") - size = name_array.pop || 1 - name = name_array.join("#") - + if name.include?("#") + name_array = name.split("#") + size = (name_array.last.to_i > 0 && name_array.size > 1) ? name_array.last.to_i : 1 + else + size = 1 + end [name, size] end + + def lock_connection(connection, lock) + @locked_connections[lock.lock_ids.first] = connection + id = lock.lock_ids.shift + lock.lock_ids.push(id) + id + end end end diff --git a/officer.gemspec b/officer.gemspec index 3b43b5a..78fd118 100644 --- a/officer.gemspec +++ b/officer.gemspec @@ -19,7 +19,6 @@ Gem::Specification.new do |gem| gem.add_dependency('json', ['>= 0']) gem.add_dependency('daemons', ['>= 0']) gem.add_dependency('choice', ['>= 0']) - #gem.add_dependency('semaphore', ['>= 0.0.1']) gem.add_development_dependency('rake', ['>= 0']) gem.add_development_dependency('rspec', ['>= 0']) diff --git a/spec/integration/officer_spec.rb b/spec/integration/officer_spec.rb index 0878f4c..224c62c 100644 --- a/spec/integration/officer_spec.rb +++ b/spec/integration/officer_spec.rb @@ -164,14 +164,14 @@ end it "should allow a client to request and release a lock" do - @client.lock("testlock").should eq({"result" => "acquired", "name" => "testlock"}) + @client.lock("testlock").should eq({"result" => "acquired", "name" => "testlock", "lock_id" => "0"}) @client.my_locks.should eq({"value"=>["testlock"], "result"=>"my_locks"}) @client.unlock("testlock") @client.my_locks.should eq({"value"=>[], "result"=>"my_locks"}) end it "should inform the client they already have a lock if they previously locked it" do - @client.lock("testlock") + @client.lock("testlock").should eq({"result" => "acquired", "name" => "testlock", "lock_id" => "0"}) @client.lock("testlock").should eq({"result" => "already_acquired", "name" => "testlock"}) end @@ -309,5 +309,67 @@ JSON.parse(@socket.gets("\n").chomp).should eq({"result" => "released", "name" => "testlock"}) end end + + describe "NEW: server support for multiple locks" do + before do + @client1 = Officer::Client.new + @client2 = Officer::Client.new + @client3 = Officer::Client.new + + @client1_src_port = @client1.instance_variable_get('@socket').addr[1] + @client2_src_port = @client2.instance_variable_get('@socket').addr[1] + @client3_src_port = @client2.instance_variable_get('@socket').addr[1] + end + + after do + @client1.send("disconnect") + @client1 = nil + @client2.send("disconnect") + @client2 = nil + @client3.send("disconnect") + @client3 = nil + end + + it "should allow a client to obtaining and releasing the number of allowed locks" do + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) + @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) + @client1.locks.should eq({"value"=>{"testlock#2"=>["127.0.0.1:#{@client1_src_port}", "127.0.0.1:#{@client2_src_port}"]}, "result"=>"locks"}) + @client2.locks.should eq({"value"=>{"testlock#2"=>["127.0.0.1:#{@client1_src_port}", "127.0.0.1:#{@client2_src_port}"]}, "result"=>"locks"}) + @client1.my_locks.should eq({"value"=>["testlock#2"], "result"=>"my_locks"}) + @client2.my_locks.should eq({"value"=>["testlock#2"], "result"=>"my_locks"}) + @client1.unlock("testlock#2") + @client2.unlock("testlock#2") + @client1.locks.should eq({"value"=>{}, "result"=>"locks"}) + end + + it "should not allow a client to request a lock that is already acquired" do + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) + @client1.lock("testlock#2").should eq({"result" => "already_acquired", "name" => "testlock#2"}) + @client2.unlock("testlock#2") + end + + it "should allow timeout if number of allowed connections is reached" do + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) + @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) + @client3.lock("testlock#2", :timeout => 0).should eq( + {"result"=>"timed_out", "name"=>"testlock#2", "queue"=>["127.0.0.1:#{@client1_src_port}", "127.0.0.1:#{@client2_src_port}"]} + ) + end + + it "should queue a connection if number of allowed connections is reached and allow connection if a lock is released" do + t = Thread.new do + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) + @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) + @client3.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) + end + + t2 = Thread.new do + puts @client1.locks + @client1.unlock + puts @client1.locks + end + sleep 5 + end + end end end From 1e5052b4ad0ba03fc10e2932a46da235fb642bd5 Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Wed, 15 Aug 2012 16:35:48 +0200 Subject: [PATCH 05/10] fixed already_acquired in lock_store --- lib/officer/lock_store.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/officer/lock_store.rb b/lib/officer/lock_store.rb index ed1e25a..a60316b 100644 --- a/lib/officer/lock_store.rb +++ b/lib/officer/lock_store.rb @@ -71,8 +71,7 @@ def acquire name, connection, options={} @acquire_counter += 1 lock = @locks[name] ||= Lock.new(name, size) - puts lock.queue.inspect - if lock.queue.count < lock.size + if lock.queue.count <= lock.size if lock.queue[0..lock.size-1].include?(connection) connection.already_acquired(name) else From 2462f55007f06c0e3bc628eeafc53849fb45949e Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Wed, 15 Aug 2012 22:57:46 +0200 Subject: [PATCH 06/10] added lock_id to locked connections --- lib/officer/lock_store.rb | 43 ++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/officer/lock_store.rb b/lib/officer/lock_store.rb index a60316b..81a6e54 100644 --- a/lib/officer/lock_store.rb +++ b/lib/officer/lock_store.rb @@ -28,6 +28,7 @@ def initialize @connections = {} # Connection => Set(name, ...) @locked_connections = {} # {lock_id => Connection} @acquire_counter = 0 + @mutex = Mutex.new end def log_state @@ -71,17 +72,18 @@ def acquire name, connection, options={} @acquire_counter += 1 lock = @locks[name] ||= Lock.new(name, size) - if lock.queue.count <= lock.size - if lock.queue[0..lock.size-1].include?(connection) - connection.already_acquired(name) - else - lock.queue << connection - lock_id = lock_connection(connection, lock) - (@connections[connection] ||= Set.new) << name - connection.acquired name, lock_id - end - elsif lock.queue.count >= lock.size + if lock.queue[0..lock.size-1].include?(connection) + return connection.already_acquired(name) + end + + if lock.queue.count < lock.size + lock.queue << connection + lock_id = lock_connection(connection, lock) + (@connections[connection] ||= Set.new) << name + + connection.acquired name, lock_id + else lock.queue << connection (@connections[connection] ||= Set.new) << name @@ -109,6 +111,7 @@ def release name, connection, options={} if index = lock.queue.index(connection) if index < lock.size lock.queue.delete_at(index) + release_connection(connection, lock) connection.released name if options[:callback] @@ -200,10 +203,22 @@ def split_name(name) end def lock_connection(connection, lock) - @locked_connections[lock.lock_ids.first] = connection - id = lock.lock_ids.shift - lock.lock_ids.push(id) - id + @mutex.synchronize do + @locked_connections[lock.lock_ids.first] = connection + lock_id = lock.lock_ids.shift + lock.lock_ids.push(lock_id) + lock_id + end + end + + def release_connection(connection, lock) + @mutex.synchronize do + if lock_id = @locked_connections.key(connection) + lock.lock_ids.delete(lock_id) + lock.lock_ids.insert(0, lock_id) + lock_id + end + end end end From b4e64abf8e5dd76bf055755d9a59402259688aea Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Wed, 15 Aug 2012 22:57:58 +0200 Subject: [PATCH 07/10] added tests for lock_id --- spec/integration/officer_spec.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/integration/officer_spec.rb b/spec/integration/officer_spec.rb index 224c62c..a1f003b 100644 --- a/spec/integration/officer_spec.rb +++ b/spec/integration/officer_spec.rb @@ -360,15 +360,14 @@ t = Thread.new do @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) - @client3.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) + @client3.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) end t2 = Thread.new do - puts @client1.locks - @client1.unlock - puts @client1.locks + sleep 2 + @client2.unlock("testlock#2") end - sleep 5 + t.join end end end From 1730c866ca2148b805fe6dbb1e1e8477ba228e3c Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Thu, 16 Aug 2012 08:09:31 +0200 Subject: [PATCH 08/10] change lock_id to id --- lib/officer/connection.rb | 2 +- spec/integration/officer_spec.rb | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/officer/connection.rb b/lib/officer/connection.rb index da464b7..ddaa110 100644 --- a/lib/officer/connection.rb +++ b/lib/officer/connection.rb @@ -37,7 +37,7 @@ module LockStoreCallbacks def acquired name, lock_id @timers.delete(name).cancel if @timers[name] - send_result 'acquired', :name => name, :lock_id => lock_id.to_s + send_result 'acquired', :name => name, :id => lock_id.to_s end def already_acquired name diff --git a/spec/integration/officer_spec.rb b/spec/integration/officer_spec.rb index a1f003b..950155e 100644 --- a/spec/integration/officer_spec.rb +++ b/spec/integration/officer_spec.rb @@ -164,14 +164,14 @@ end it "should allow a client to request and release a lock" do - @client.lock("testlock").should eq({"result" => "acquired", "name" => "testlock", "lock_id" => "0"}) + @client.lock("testlock").should eq({"result" => "acquired", "name" => "testlock", "id" => "0"}) @client.my_locks.should eq({"value"=>["testlock"], "result"=>"my_locks"}) @client.unlock("testlock") @client.my_locks.should eq({"value"=>[], "result"=>"my_locks"}) end it "should inform the client they already have a lock if they previously locked it" do - @client.lock("testlock").should eq({"result" => "acquired", "name" => "testlock", "lock_id" => "0"}) + @client.lock("testlock").should eq({"result" => "acquired", "name" => "testlock", "id" => "0"}) @client.lock("testlock").should eq({"result" => "already_acquired", "name" => "testlock"}) end @@ -331,8 +331,8 @@ end it "should allow a client to obtaining and releasing the number of allowed locks" do - @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) - @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "0"}) + @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "1"}) @client1.locks.should eq({"value"=>{"testlock#2"=>["127.0.0.1:#{@client1_src_port}", "127.0.0.1:#{@client2_src_port}"]}, "result"=>"locks"}) @client2.locks.should eq({"value"=>{"testlock#2"=>["127.0.0.1:#{@client1_src_port}", "127.0.0.1:#{@client2_src_port}"]}, "result"=>"locks"}) @client1.my_locks.should eq({"value"=>["testlock#2"], "result"=>"my_locks"}) @@ -343,14 +343,14 @@ end it "should not allow a client to request a lock that is already acquired" do - @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "0"}) @client1.lock("testlock#2").should eq({"result" => "already_acquired", "name" => "testlock#2"}) @client2.unlock("testlock#2") end it "should allow timeout if number of allowed connections is reached" do - @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) - @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "0"}) + @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "1"}) @client3.lock("testlock#2", :timeout => 0).should eq( {"result"=>"timed_out", "name"=>"testlock#2", "queue"=>["127.0.0.1:#{@client1_src_port}", "127.0.0.1:#{@client2_src_port}"]} ) @@ -358,9 +358,9 @@ it "should queue a connection if number of allowed connections is reached and allow connection if a lock is released" do t = Thread.new do - @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "0"}) - @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) - @client3.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "lock_id" => "1"}) + @client1.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "0"}) + @client2.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "1"}) + @client3.lock("testlock#2").should eq({"result" => "acquired", "name" => "testlock#2", "id" => "1"}) end t2 = Thread.new do From c5ceaa35b13191437d80699af1947751d40246d3 Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Tue, 21 Aug 2012 11:53:39 +0200 Subject: [PATCH 09/10] add semaphore class to separate client communication --- lib/officer.rb | 1 + lib/officer/semaphore.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 lib/officer/semaphore.rb diff --git a/lib/officer.rb b/lib/officer.rb index 2a0ad6d..6a6ff5e 100644 --- a/lib/officer.rb +++ b/lib/officer.rb @@ -23,3 +23,4 @@ require 'officer/runner' require 'officer/server' require 'officer/client' +require 'officer/semaphore' diff --git a/lib/officer/semaphore.rb b/lib/officer/semaphore.rb new file mode 100644 index 0000000..8c4cf98 --- /dev/null +++ b/lib/officer/semaphore.rb @@ -0,0 +1,27 @@ +module Officer + class Semaphore + attr_accessor :size + + def initialize(host, port, name, size) + size ||= 1 + + @client = Officer::Client.new(:host => host, :port => port) + @name = "#{name}##{size}" + @size = size + end + + def lock + @client.lock(@name) + end + + def unlock + @client.unlock(@name) + end + + def synchronize + @client.with_lock @name do + yield + end + end + end +end \ No newline at end of file From 70b36c1cb98a87b468b32c1af9cec839aaae23cc Mon Sep 17 00:00:00 2001 From: Luis Doubrava Date: Tue, 21 Aug 2012 12:00:21 +0200 Subject: [PATCH 10/10] bump version to 0.10.2 --- lib/officer/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/officer/version.rb b/lib/officer/version.rb index c43f12b..dcaddce 100644 --- a/lib/officer/version.rb +++ b/lib/officer/version.rb @@ -1,3 +1,3 @@ module Officer - VERSION = "0.10.1" + VERSION = "0.10.2" end