created Redistat::Synchronize mixin to help with thread-safety

This commit is contained in:
2011-04-15 14:03:26 +01:00
parent 0a7abe935e
commit 5d92c1dbae
3 changed files with 125 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ require 'rubygems'
require 'date'
require 'time'
require 'digest/sha1'
require 'monitor'
# Active Support 2.x or 3.x
require 'active_support'
@@ -16,6 +17,7 @@ require 'redis'
require 'json'
require 'redistat/options'
require 'redistat/synchronize'
require 'redistat/connection'
require 'redistat/database'
require 'redistat/collection'
@@ -47,6 +49,14 @@ module Redistat
class << self
def thread_safe
Synchronize.thread_safe
end
def thread_safe=(value)
Synchronize.thread_safe = value
end
def connection(ref = nil)
Connection.get(ref)
end

View File

@@ -0,0 +1,51 @@
require 'monitor'
module Redistat
module Synchronize
class << self
def included(base)
base.send(:include, InstanceMethods)
end
def monitor
@monitor ||= Monitor.new
end
def thread_safe
monitor.synchronize do
@thread_safe ||= false
end
end
def thread_safe=(value)
monitor.synchronize do
@thread_safe = value
end
end
end # << self
module InstanceMethods
def thread_safe
Synchronize.thread_safe
end
def thread_safe=(value)
Synchronize.thread_safe = value
end
def monitor
Synchronize.monitor
end
def synchronize(&block)
if thread_safe
monitor.synchronize(&block)
else
block.call
end
end
end # InstanceMethods
end
end

64
spec/synchronize_spec.rb Normal file
View File

@@ -0,0 +1,64 @@
require "spec_helper"
describe Redistat::Synchronize do
it { should respond_to(:monitor) }
it { should respond_to(:thread_safe) }
it { should respond_to(:thread_safe=) }
describe "instanciated class with Redistat::Synchronize included" do
subject { SynchronizeSpecHelper.new }
it { should respond_to(:monitor) }
it { should respond_to(:thread_safe) }
it { should respond_to(:thread_safe=) }
it { should respond_to(:synchronize) }
end
describe "#synchronize method" do
before(:each) do
Redistat::Synchronize.instance_variable_set("@thread_safe", nil)
@obj = SynchronizeSpecHelper.new
end
it "should share single Monitor object across all objects" do
@obj.monitor.should == Redistat::Synchronize.monitor
end
it "should share thread_safe option across all objects" do
obj2 = SynchronizeSpecHelper.new
Redistat::Synchronize.thread_safe.should be_false
@obj.thread_safe.should be_false
obj2.thread_safe.should be_false
@obj.thread_safe = true
Redistat::Synchronize.thread_safe.should be_true
@obj.thread_safe.should be_true
obj2.thread_safe.should be_true
end
it "should not synchronize when thread_safe is disabled" do
# monitor receives :synchronize twice cause #thread_safe is _always_ synchronized
Redistat::Synchronize.monitor.should_receive(:synchronize).twice
@obj.thread_safe.should be_false # first #synchronize call
@obj.synchronize { 'foo' } # one #synchronize call while checking #thread_safe
end
it "should synchronize when thread_safe is enabled" do
Monitor.class_eval {
# we're stubbing synchronize to ensure it's being called correctly, but still need it :P
alias :real_synchronize :synchronize
}
Redistat::Synchronize.monitor.should_receive(:synchronize).with.exactly(4).times.and_return { |block|
Redistat::Synchronize.monitor.real_synchronize(&block)
}
@obj.thread_safe.should be_false # first synchronize call
Redistat::Synchronize.thread_safe = true # second synchronize call
@obj.synchronize { 'foo' } # two synchronize calls, once while checking thread_safe, once to call black
end
end
end
class SynchronizeSpecHelper
include Redistat::Synchronize
end