diff --git a/lib/semian.rb b/lib/semian.rb index 9d4076e04..a88651310 100644 --- a/lib/semian.rb +++ b/lib/semian.rb @@ -124,7 +124,8 @@ def register(name, tickets:, permissions: 0660, timeout: 0, error_threshold:, er error_threshold: error_threshold, error_timeout: error_timeout, exceptions: Array(exceptions) + [::Semian::BaseError], - implementation: ::Semian::Simple, + permissions: permissions, + implementation: Semian.semaphores_enabled? ? ::Semian::SysV : ::Semian::Simple, ) resource = Resource.new(name, tickets: tickets, permissions: permissions, timeout: timeout) resources[name] = ProtectedResource.new(resource, circuit_breaker) @@ -160,6 +161,10 @@ def resources require 'semian/simple_integer' require 'semian/simple_state' if Semian.semaphores_enabled? + require 'semian/sysv_shared_memory' + require 'semian/sysv_sliding_window' + require 'semian/sysv_integer' + require 'semian/sysv_state' require 'semian/semian' else Semian::MAX_TICKETS = 0 diff --git a/lib/semian/circuit_breaker.rb b/lib/semian/circuit_breaker.rb index f732ceb75..349dab984 100644 --- a/lib/semian/circuit_breaker.rb +++ b/lib/semian/circuit_breaker.rb @@ -2,16 +2,20 @@ module Semian class CircuitBreaker #:nodoc: extend Forwardable - def initialize(name, exceptions:, success_threshold:, error_threshold:, error_timeout:, implementation:) + def initialize(name, exceptions:, success_threshold:, error_threshold:, error_timeout:, permissions:, implementation:) @name = name.to_sym @success_count_threshold = success_threshold @error_count_threshold = error_threshold @error_timeout = error_timeout @exceptions = exceptions - @errors = implementation::SlidingWindow.new(max_size: @error_count_threshold) - @successes = implementation::Integer.new - @state = implementation::State.new + @errors = implementation::SlidingWindow.new(max_size: @error_count_threshold, + name: "#{name}_sliding_window", + permissions: permissions) + @successes = implementation::Integer.new(name: "#{name}_integer", + permissions: permissions) + @state = implementation::State.new(name: "#{name}_state", + permissions: permissions) end def acquire diff --git a/lib/semian/simple_integer.rb b/lib/semian/simple_integer.rb index aff732204..cb4403e9a 100644 --- a/lib/semian/simple_integer.rb +++ b/lib/semian/simple_integer.rb @@ -3,7 +3,7 @@ module Simple class Integer #:nodoc: attr_accessor :value - def initialize + def initialize(**) reset end diff --git a/lib/semian/simple_sliding_window.rb b/lib/semian/simple_sliding_window.rb index 2507bfb8d..bfd57e545 100644 --- a/lib/semian/simple_sliding_window.rb +++ b/lib/semian/simple_sliding_window.rb @@ -1,3 +1,5 @@ +require 'forwardable' + module Semian module Simple class SlidingWindow #:nodoc: @@ -10,7 +12,7 @@ class SlidingWindow #:nodoc: # like this: if @max_size = 4, current time is 10, @window =[5,7,9,10]. # Another push of (11) at 11 sec would make @window [7,9,10,11], shifting off 5. - def initialize(max_size:) + def initialize(max_size:, **) @max_size = max_size @window = [] end diff --git a/lib/semian/simple_state.rb b/lib/semian/simple_state.rb index d97f49607..eff76a982 100644 --- a/lib/semian/simple_state.rb +++ b/lib/semian/simple_state.rb @@ -1,11 +1,12 @@ module Semian module Simple class State #:nodoc: - def initialize + def initialize(**) reset end - attr_reader :value + attr_accessor :value + private :value= def open? value == :open @@ -20,15 +21,15 @@ def half_open? end def open - @value = :open + self.value = :open end def close - @value = :closed + self.value = :closed end def half_open - @value = :half_open + self.value = :half_open end def reset diff --git a/lib/semian/sysv_integer.rb b/lib/semian/sysv_integer.rb new file mode 100644 index 000000000..72554cc7b --- /dev/null +++ b/lib/semian/sysv_integer.rb @@ -0,0 +1,12 @@ +module Semian + module SysV + class Integer < Semian::Simple::Integer #:nodoc: + include SysVSharedMemory + + def initialize(name:, permissions:) + data_layout = [:int] + super() unless acquire_memory_object(name, data_layout, permissions) + end + end + end +end diff --git a/lib/semian/sysv_shared_memory.rb b/lib/semian/sysv_shared_memory.rb new file mode 100644 index 000000000..58abe7ee2 --- /dev/null +++ b/lib/semian/sysv_shared_memory.rb @@ -0,0 +1,50 @@ +module Semian + module SysVSharedMemory #:nodoc: + def self.included(base) + def base.do_with_sync(*names) + names.each do |name| + new_name = "#{name}_inner".freeze.to_sym + alias_method new_name, name + private new_name + define_method(name) do |*args, &block| + synchronize do + method(new_name).call(*args, &block) + end + end + end + end + end + + def semid + -1 + end + + def shmid + -1 + end + + def synchronize + yield if block_given? + end + + def destroy + super + end + + private + + def acquire_memory_object(*) + # Concrete classes must call this method before accessing shared memory + # If SysV is enabled, a C method overrides this stub and returns true if acquiring succeeds + false + end + + def bind_initialize_memory_callback + # Concrete classes must implement this in a subclass in C to bind a callback function of type + # void (*initialize_memory)(size_t byte_size, void *dest, void *prev_data, size_t prev_data_byte_size); + # to location ptr->initialize_memory, where ptr is a semian_shm_object* + # It is called when memory needs to be initialized or resized, possibly using previous memory + raise NotImplementedError + end + end +end diff --git a/lib/semian/sysv_sliding_window.rb b/lib/semian/sysv_sliding_window.rb new file mode 100644 index 000000000..4c54c0225 --- /dev/null +++ b/lib/semian/sysv_sliding_window.rb @@ -0,0 +1,12 @@ +module Semian + module SysV + class SlidingWindow < Semian::Simple::SlidingWindow #:nodoc: + include SysVSharedMemory + + def initialize(max_size:, name:, permissions:) + data_layout = [:int, :int] + [:long] * max_size + super(max_size: max_size) unless acquire_memory_object(name, data_layout, permissions) + end + end + end +end diff --git a/lib/semian/sysv_state.rb b/lib/semian/sysv_state.rb new file mode 100644 index 000000000..fc338a9a3 --- /dev/null +++ b/lib/semian/sysv_state.rb @@ -0,0 +1,31 @@ +require 'forwardable' + +module Semian + module SysV + class State < Semian::Simple::State #:nodoc: + include SysVSharedMemory + extend Forwardable + + SYM_TO_NUM = {closed: 0, open: 1, half_open: 2}.freeze + NUM_TO_SYM = SYM_TO_NUM.invert.freeze + + def_delegators :@integer, :semid, :shmid, :synchronize, :acquire_memory_object, + :bind_initialize_memory_callback, :destroy + private :acquire_memory_object, :bind_initialize_memory_callback + + def initialize(name:, permissions:) + @integer = Semian::SysV::Integer.new(name: name, permissions: permissions) + 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 + end + end +end diff --git a/test/simple_state_test.rb b/test/simple_state_test.rb index 6f629744c..edab7c227 100644 --- a/test/simple_state_test.rb +++ b/test/simple_state_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class TestSimpleEnum < MiniTest::Unit::TestCase +class TestSimpleState < MiniTest::Unit::TestCase CLASS = ::Semian::Simple::State def setup diff --git a/test/sysv_integer_test.rb b/test/sysv_integer_test.rb new file mode 100644 index 000000000..afbdf5ea7 --- /dev/null +++ b/test/sysv_integer_test.rb @@ -0,0 +1,16 @@ +require 'test_helper' + +class TestSysVInteger < MiniTest::Unit::TestCase + KLASS = ::Semian::SysV::Integer + + def setup + @integer = KLASS.new(name: 'TestSysVInteger', permissions: 0660) + @integer.reset + end + + def teardown + @integer.destroy + end + + include TestSimpleInteger::IntegerTestCases +end diff --git a/test/sysv_sliding_window_test.rb b/test/sysv_sliding_window_test.rb new file mode 100644 index 000000000..ebedfb1f7 --- /dev/null +++ b/test/sysv_sliding_window_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +class TestSysVSlidingWindow < MiniTest::Unit::TestCase + KLASS = ::Semian::SysV::SlidingWindow + + def setup + @sliding_window = KLASS.new(max_size: 6, + name: 'TestSysVSlidingWindow', + permissions: 0660) + @sliding_window.clear + end + + def teardown + @sliding_window.destroy + end + + include TestSimpleSlidingWindow::SlidingWindowTestCases + + private + + include TestSimpleSlidingWindow::SlidingWindowUtilityMethods +end diff --git a/test/sysv_state_test.rb b/test/sysv_state_test.rb new file mode 100644 index 000000000..a2f8496ec --- /dev/null +++ b/test/sysv_state_test.rb @@ -0,0 +1,35 @@ +require 'test_helper' + +class TestSysVState < MiniTest::Unit::TestCase + KLASS = ::Semian::SysV::State + + def setup + @state = KLASS.new(name: 'TestSysVState', + permissions: 0660) + @state.reset + end + + def teardown + @state.destroy + end + + include TestSimpleState::StateTestCases + + 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