From 2e9cd7ac29721c31ab00e56601765727aea68322 Mon Sep 17 00:00:00 2001 From: Kye Wei Date: Thu, 12 Nov 2015 16:32:39 +0000 Subject: [PATCH] Change to State from Enum, updated to match merged PR, removed Mutex code --- lib/semian.rb | 29 ++------------- lib/semian/circuit_breaker.rb | 28 ++++++--------- lib/semian/shared_memory_object.rb | 2 -- lib/semian/simple_enum.rb | 42 ---------------------- lib/semian/simple_integer.rb | 2 -- lib/semian/simple_sliding_window.rb | 3 -- lib/semian/simple_state.rb | 45 +++++++++++++++++++++++ lib/semian/sysv_enum.rb | 10 ------ lib/semian/sysv_state.rb | 56 +++++++++++++++++++++++++++++ test/circuit_breaker_test.rb | 2 +- test/simple_enum_test.rb | 47 ------------------------ test/simple_state_test.rb | 45 +++++++++++++++++++++++ test/sysv_enum_test.rb | 49 ------------------------- test/sysv_integer_test.rb | 32 +++-------------- test/sysv_sliding_window_test.rb | 43 ++++++---------------- test/sysv_state_test.rb | 44 +++++++++++++++++++++++ 16 files changed, 218 insertions(+), 261 deletions(-) delete mode 100644 lib/semian/simple_enum.rb create mode 100644 lib/semian/simple_state.rb delete mode 100644 lib/semian/sysv_enum.rb create mode 100644 lib/semian/sysv_state.rb delete mode 100644 test/simple_enum_test.rb create mode 100644 test/simple_state_test.rb delete mode 100644 test/sysv_enum_test.rb create mode 100644 test/sysv_state_test.rb diff --git a/lib/semian.rb b/lib/semian.rb index 90f8a507..c16ff802 100644 --- a/lib/semian.rb +++ b/lib/semian.rb @@ -150,31 +150,6 @@ def destroy(name) def resources @resources ||= {} end - - module ReentrantMutex - attr_reader :mutex - - def call_with_mutex - @mutex ||= Monitor.new - @mutex.synchronize do - yield if block_given? - end - end - - def self.included(base) - def base.surround_with_mutex(*names) - names.each do |name| - new_name = "#{name}_inner".freeze - alias_method new_name, name - define_method(name) do |*args, &block| - call_with_mutex do - method(new_name).call(*args, &block) - end - end - end - end - end - end end require 'semian/resource' @@ -185,10 +160,10 @@ def base.surround_with_mutex(*names) require 'semian/shared_memory_object' require 'semian/simple_sliding_window' require 'semian/simple_integer' -require 'semian/simple_enum' +require 'semian/simple_state' require 'semian/sysv_sliding_window' require 'semian/sysv_integer' -require 'semian/sysv_enum' +require 'semian/sysv_state' if Semian.semaphores_enabled? require 'semian/semian' else diff --git a/lib/semian/circuit_breaker.rb b/lib/semian/circuit_breaker.rb index 27174a96..b6567314 100644 --- a/lib/semian/circuit_breaker.rb +++ b/lib/semian/circuit_breaker.rb @@ -1,5 +1,7 @@ module Semian class CircuitBreaker #:nodoc: + extend Forwardable + def initialize(name, exceptions:, success_threshold:, error_threshold:, error_timeout:, permissions:, implementation:) @name = name.to_s @success_count_threshold = success_threshold @@ -12,9 +14,8 @@ def initialize(name, exceptions:, success_threshold:, error_threshold:, error_ti permissions: permissions) @successes = implementation::Integer.new(name: "#{name}_sysv_integer", permissions: permissions) - @state = implementation::Enum.new(symbol_list: [:closed, :half_open, :open], - name: "#{name}_sysv_enum", - permissions: permissions) + @state = implementation::State.new(name: "#{name}_sysv_state", + permissions: permissions) end def acquire @@ -67,33 +68,24 @@ def destroy private - def closed? - @state.value == :closed - end + def_delegators :@state, :closed?, :open?, :half_open? + private :closed?, :open?, :half_open? def close log_state_transition(:closed) - @state.value = :closed + @state.close @errors.clear end - def open? - @state.value == :open - end - def open log_state_transition(:open) - @state.value = :open - end - - def half_open? - @state.value == :half_open + @state.open end def half_open log_state_transition(:half_open) - @state.value = :half_open - @successes.value = 0 + @state.half_open + @successes.reset end def success_threshold_reached? diff --git a/lib/semian/shared_memory_object.rb b/lib/semian/shared_memory_object.rb index 660cc1c0..bad3433f 100644 --- a/lib/semian/shared_memory_object.rb +++ b/lib/semian/shared_memory_object.rb @@ -1,7 +1,5 @@ module Semian class SharedMemoryObject #:nodoc: - include ReentrantMutex - @type_size = {} def self.sizeof(type) size = (@type_size[type.to_sym] ||= (respond_to?(:_sizeof) ? _sizeof(type.to_sym) : 0)) diff --git a/lib/semian/simple_enum.rb b/lib/semian/simple_enum.rb deleted file mode 100644 index 7f6ab740..00000000 --- a/lib/semian/simple_enum.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'forwardable' - -module Semian - module Simple - class Enum < SharedMemoryObject #:nodoc: - extend Forwardable - - def_delegators :@integer, :semid, :shmid, :execute_atomically, :transaction, - :shared?, :destroy, :acquire_memory_object, :bind_init_fn - private :shared?, :acquire_memory_object, :bind_init_fn - - def initialize(symbol_list:) - @integer = Semian::Simple::Integer.new - initialize_lookup(symbol_list) - end - - def increment(val = 1) - @integer.value = (@integer.value + val) % @sym_to_num.size - value - end - - def value - @num_to_sym.fetch(@integer.value) - end - - def value=(sym) - @integer.value = @sym_to_num.fetch(sym) - end - - private - - def initialize_lookup(symbol_list) - # Assume symbol_list[0] is mapped to 0 - # Cannot just use #object_id since #object_id for symbols is different in every run - # For now, implement a C-style enum type backed by integers - - @sym_to_num = Hash[symbol_list.each_with_index.to_a] - @num_to_sym = @sym_to_num.invert - end - end - end -end diff --git a/lib/semian/simple_integer.rb b/lib/semian/simple_integer.rb index 8778681a..ea27d6eb 100644 --- a/lib/semian/simple_integer.rb +++ b/lib/semian/simple_integer.rb @@ -22,8 +22,6 @@ def destroy @value = 0 end end - - surround_with_mutex :value, :value=, :increment end end end diff --git a/lib/semian/simple_sliding_window.rb b/lib/semian/simple_sliding_window.rb index 0efe8137..e551c8f6 100644 --- a/lib/semian/simple_sliding_window.rb +++ b/lib/semian/simple_sliding_window.rb @@ -42,9 +42,6 @@ def destroy clear end end - - surround_with_mutex :size, :pop, :shift, :first, :last, :max_size, - :resize_to, :<<, :push, :clear end end end diff --git a/lib/semian/simple_state.rb b/lib/semian/simple_state.rb new file mode 100644 index 00000000..59d6774a --- /dev/null +++ b/lib/semian/simple_state.rb @@ -0,0 +1,45 @@ +require 'forwardable' + +module Semian + module Simple + class State < SharedMemoryObject #:nodoc: + def initialize + reset + end + + attr_reader :value + + def open? + value == :open + end + + def closed? + value == :closed + end + + def half_open? + value == :half_open + end + + def open + @value = :open + end + + def close + @value = :closed + end + + def half_open + @value = :half_open + end + + def reset + close + end + + def destroy + reset + end + end + end +end diff --git a/lib/semian/sysv_enum.rb b/lib/semian/sysv_enum.rb deleted file mode 100644 index 6122eb65..00000000 --- a/lib/semian/sysv_enum.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Semian - module SysV - class Enum < Semian::Simple::Enum #:nodoc: - def initialize(symbol_list:, name:, permissions:) - @integer = Semian::SysV::Integer.new(name: name, permissions: permissions) - initialize_lookup(symbol_list) - end - end - end -end diff --git a/lib/semian/sysv_state.rb b/lib/semian/sysv_state.rb new file mode 100644 index 00000000..2d0f4568 --- /dev/null +++ b/lib/semian/sysv_state.rb @@ -0,0 +1,56 @@ +module Semian + module SysV + class State < Semian::Simple::State #:nodoc: + extend Forwardable + + def_delegators :@integer, :semid, :shmid, :execute_atomically, :transaction, + :shared?, :acquire_memory_object, :bind_init_fn + private :shared?, :acquire_memory_object, :bind_init_fn + + def initialize(name:, permissions:) + @integer = Semian::SysV::Integer.new(name: name, permissions: permissions) + initialize_lookup([:closed, :open, :half_open]) + end + + def open + self.value = :open + end + + def close + self.value = :closed + end + + def half_open + self.value = :half_open + end + + def reset + close + end + + def destroy + reset + @integer.destroy + end + + def value + @num_to_sym.fetch(@integer.value) { raise ArgumentError } + end + + private + + def value=(sym) + @integer.value = @sym_to_num.fetch(sym) { raise ArgumentError } + end + + def initialize_lookup(symbol_list) + # Assume symbol_list[0] is mapped to 0 + # Cannot just use #object_id since #object_id for symbols is different in every run + # For now, implement a C-style enum type backed by integers + + @sym_to_num = Hash[symbol_list.each_with_index.to_a] + @num_to_sym = @sym_to_num.invert + end + end + end +end diff --git a/test/circuit_breaker_test.rb b/test/circuit_breaker_test.rb index 305c31b0..e79964eb 100644 --- a/test/circuit_breaker_test.rb +++ b/test/circuit_breaker_test.rb @@ -138,7 +138,7 @@ def test_shared_fresh_worker_killed_should_not_reset_circuit_breaker_data Process.waitall fork do Semian.register(:unique_res, tickets: 1, exceptions: [SomeError], error_threshold: 2, error_timeout: 5, success_threshold: 1) - assert_circuit_opened + assert_circuit_opened Semian[:unique_res] end Process.waitall diff --git a/test/simple_enum_test.rb b/test/simple_enum_test.rb deleted file mode 100644 index 6846a6dc..00000000 --- a/test/simple_enum_test.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'test_helper' - -class TestSimpleEnum < MiniTest::Unit::TestCase - CLASS = ::Semian::Simple::Enum - - def setup - @enum = CLASS.new(symbol_list: [:one, :two, :three]) - end - - def teardown - @enum.destroy - end - - module EnumTestCases - def test_assigning - old = @enum.value - @enum.value = @enum.value - assert_equal old, @enum.value - @enum.value = :two - assert_equal :two, @enum.value - end - - def test_iterate_enum - @enum.value = :one - @enum.increment - assert_equal :two, @enum.value - @enum.increment - assert_equal :three, @enum.value - @enum.increment - assert_equal :one, @enum.value - @enum.increment(2) - assert_equal :three, @enum.value - @enum.increment(4) - assert_equal :one, @enum.value - @enum.increment(0) - assert_equal :one, @enum.value - end - - def test_will_throw_error_when_invalid_symbol_given - assert_raises KeyError do - @enum.value = :four - end - end - end - - include EnumTestCases -end diff --git a/test/simple_state_test.rb b/test/simple_state_test.rb new file mode 100644 index 00000000..edab7c22 --- /dev/null +++ b/test/simple_state_test.rb @@ -0,0 +1,45 @@ +require 'test_helper' + +class TestSimpleState < MiniTest::Unit::TestCase + CLASS = ::Semian::Simple::State + + def setup + @state = CLASS.new + end + + def teardown + @state.destroy + end + + module StateTestCases + def test_start_closed? + assert @state.closed? + end + + def test_open + @state.open + assert @state.open? + assert_equal @state.value, :open + end + + def test_close + @state.close + assert @state.closed? + assert_equal @state.value, :closed + end + + def test_half_open + @state.half_open + assert @state.half_open? + assert_equal @state.value, :half_open + end + + def test_reset + @state.reset + assert @state.closed? + assert_equal @state.value, :closed + end + end + + include StateTestCases +end diff --git a/test/sysv_enum_test.rb b/test/sysv_enum_test.rb deleted file mode 100644 index ecf64784..00000000 --- a/test/sysv_enum_test.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'test_helper' - -class TestSysVEnum < MiniTest::Unit::TestCase - # Emulate sharedness to test correctness against real SysVAtomicEnum class - class FakeSysVAtomicEnum < Semian::Simple::Enum - class << self - attr_accessor :resources - end - self.resources = {} - attr_accessor :name - def self.new(symbol_list:, name:, permissions:) - obj = resources[name] ||= super - obj.name = name - obj - end - - def destroy - self.class.resources.delete(@name) - end - - def shared? - true - end - end - - CLASS = ::Semian::SysV::Enum - - def setup - @enum = CLASS.new(symbol_list: [:one, :two, :three], - name: 'TestAtomicEnum', - permissions: 0660) - end - - def teardown - @enum.destroy - end - - include TestSimpleEnum::EnumTestCases - - def test_memory_is_shared - assert_equal :one, @enum.value - @enum.value = :three - - enum_2 = CLASS.new(symbol_list: [:one, :two, :three], - name: 'TestAtomicEnum', - permissions: 0660) - assert_equal :three, enum_2.value - end -end diff --git a/test/sysv_integer_test.rb b/test/sysv_integer_test.rb index a36bc653..c445193d 100644 --- a/test/sysv_integer_test.rb +++ b/test/sysv_integer_test.rb @@ -1,32 +1,10 @@ require 'test_helper' class TestSysVInteger < MiniTest::Unit::TestCase - # Emulate sharedness to test correctness against real SysVAtomicInteger class - class FakeSysVAtomicInteger < Semian::Simple::Integer - class << self - attr_accessor :resources - end - self.resources = {} - attr_accessor :name - def self.new(name:, permissions:) - obj = resources[name] ||= super - obj.name = name - obj - end - - def destroy - self.class.resources.delete(@name) - end - - def shared? - true - end - end - CLASS = ::Semian::SysV::Integer def setup - @integer = CLASS.new(name: 'TestAtomicInteger', permissions: 0660) + @integer = CLASS.new(name: 'TestSysVInteger', permissions: 0660) @integer.value = 0 end @@ -37,7 +15,7 @@ def teardown include TestSimpleInteger::IntegerTestCases def test_memory_is_shared - integer_2 = CLASS.new(name: 'TestAtomicInteger', permissions: 0660) + integer_2 = CLASS.new(name: 'TestSysVInteger', permissions: 0660) integer_2.value = 100 assert_equal 100, @integer.value @integer.value = 200 @@ -48,10 +26,10 @@ def test_memory_is_shared def test_memory_not_reset_when_at_least_one_worker_using_it @integer.value = 109 - integer_2 = CLASS.new(name: 'TestAtomicInteger', permissions: 0660) + integer_2 = CLASS.new(name: 'TestSysVInteger', permissions: 0660) assert_equal @integer.value, integer_2.value pid = fork do - integer_3 = CLASS.new(name: 'TestAtomicInteger', permissions: 0660) + integer_3 = CLASS.new(name: 'TestSysVInteger', permissions: 0660) assert_equal 109, integer_3.value sleep end @@ -59,7 +37,7 @@ def test_memory_not_reset_when_at_least_one_worker_using_it Process.kill("KILL", pid) Process.waitall fork do - integer_3 = CLASS.new(name: 'TestAtomicInteger', permissions: 0660) + integer_3 = CLASS.new(name: 'TestSysVInteger', permissions: 0660) assert_equal 109, integer_3.value end Process.waitall diff --git a/test/sysv_sliding_window_test.rb b/test/sysv_sliding_window_test.rb index 888f6d99..3ee7be64 100644 --- a/test/sysv_sliding_window_test.rb +++ b/test/sysv_sliding_window_test.rb @@ -1,34 +1,11 @@ require 'test_helper' class TestSysVSlidingWindow < MiniTest::Unit::TestCase - # Emulate sharedness to test correctness against real SysVSlidingWindow class - class FakeSysVSlidingWindow < Semian::Simple::SlidingWindow - class << self - attr_accessor :resources - end - self.resources = {} - attr_accessor :name - def self.new(max_size:, name:, permissions:) - obj = resources[name] ||= super - obj.name = name - obj.resize_to(max_size) - obj - end - - def destroy - self.class.resources.delete(@name) - end - - def shared? - true - end - end - CLASS = ::Semian::SysV::SlidingWindow def setup @sliding_window = CLASS.new(max_size: 6, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) @sliding_window.clear end @@ -47,7 +24,7 @@ def test_forcefully_killing_worker_holding_on_to_semaphore_releases_it pid = fork do sliding_window_2 = CLASS.new(max_size: 6, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) sliding_window_2.execute_atomically { sleep } end @@ -65,7 +42,7 @@ def test_forcefully_killing_worker_holding_on_to_semaphore_releases_it def test_sliding_window_memory_is_actually_shared assert_equal 0, @sliding_window.size sliding_window_2 = CLASS.new(max_size: 6, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) assert_equal 0, sliding_window_2.size @@ -85,14 +62,14 @@ def test_sliding_window_memory_is_actually_shared def test_restarting_worker_should_not_reset_queue @sliding_window << 10 << 20 << 30 sliding_window_2 = CLASS.new(max_size: 6, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) assert_correct_first_and_last_and_size(@sliding_window, 10, 30, 3, 6) sliding_window_2.pop assert_sliding_windows_in_sync(@sliding_window, sliding_window_2) sliding_window_3 = CLASS.new(max_size: 6, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) assert_correct_first_and_last_and_size(@sliding_window, 10, 20, 2, 6) sliding_window_3.pop @@ -105,14 +82,14 @@ def test_other_workers_automatically_switching_to_new_memory_resizing_up_or_down # Test explicit resizing, and resizing through making new memory associations sliding_window_2 = CLASS.new(max_size: 4, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) sliding_window_2 << 80 << 90 << 100 << 110 << 120 assert_correct_first_and_last_and_size(@sliding_window, 90, 120, 4, 4) assert_sliding_windows_in_sync(@sliding_window, sliding_window_2) sliding_window_2 = CLASS.new(max_size: 3, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) assert_sliding_windows_in_sync(@sliding_window, sliding_window_2) assert_correct_first_and_last_and_size(@sliding_window, 100, 120, 3, 3) @@ -122,7 +99,7 @@ def test_other_workers_automatically_switching_to_new_memory_resizing_up_or_down assert_correct_first_and_last_and_size(@sliding_window, 110, 120, 2, 2) sliding_window_2 = CLASS.new(max_size: 4, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) assert_sliding_windows_in_sync(@sliding_window, sliding_window_2) assert_correct_first_and_last_and_size(@sliding_window, 110, 120, 2, 4) @@ -133,7 +110,7 @@ def test_other_workers_automatically_switching_to_new_memory_resizing_up_or_down assert_correct_first_and_last_and_size(@sliding_window, 110, 130, 3, 6) sliding_window_2 = CLASS.new(max_size: 2, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) assert_sliding_windows_in_sync(@sliding_window, sliding_window_2) assert_correct_first_and_last_and_size(@sliding_window, 120, 130, 2, 2) @@ -143,7 +120,7 @@ def test_other_workers_automatically_switching_to_new_memory_resizing_up_or_down assert_correct_first_and_last_and_size(@sliding_window, 120, 130, 2, 4) sliding_window_2 = CLASS.new(max_size: 6, - name: 'TestSlidingWindow', + name: 'TestSysVSlidingWindow', permissions: 0660) assert_sliding_windows_in_sync(@sliding_window, sliding_window_2) assert_correct_first_and_last_and_size(@sliding_window, 120, 130, 2, 6) diff --git a/test/sysv_state_test.rb b/test/sysv_state_test.rb new file mode 100644 index 00000000..a5b77e3b --- /dev/null +++ b/test/sysv_state_test.rb @@ -0,0 +1,44 @@ +require 'test_helper' + +class TestSysVState < MiniTest::Unit::TestCase + CLASS = ::Semian::SysV::State + + def setup + @state = CLASS.new(name: 'TestSysVState', + permissions: 0660) + end + + def teardown + @state.destroy + end + + include TestSimpleState::StateTestCases + + def test_memory_is_shared + assert_equal :closed, @state.value + @state.open + + state_2 = CLASS.new(name: 'TestSysVState', + permissions: 0660) + assert_equal :open, state_2.value + assert state_2.open? + end + + def test_will_throw_error_when_invalid_symbol_given + # May occur if underlying integer gets into bad state + integer = @state.instance_eval "@integer" + integer.value = 100 + assert_raises ArgumentError do + @state.value + end + assert_raises ArgumentError do + @state.open? + end + assert_raises ArgumentError do + @state.half_open? + end + assert_raises ArgumentError do + @state.closed? + end + end +end