diff --git a/lib/process_shared/shared_array.rb b/lib/process_shared/shared_array.rb new file mode 100644 index 0000000..5454663 --- /dev/null +++ b/lib/process_shared/shared_array.rb @@ -0,0 +1,68 @@ +module ProcessShared + class SharedArray < SharedMemory + include Enumerable + + # A fixed-size array in shared memory. Processes forked from this + # one will be able to read and write shared data to the array. + # Access should be synchronized using a {Mutex}, {Semaphore}, or + # other means. + # + # Note that {Enumerable} methods such as {#map}, {#sort}, + # etc. return new {Array} objects rather than modifying the shared + # array. + # + # @param [Symbol] type_or_count the data type as a symbol + # understood by FFI (e.g. :int, :double) + # + # @param [Integer] count number of array elements + def initialize(type_or_count = 1, count = 1) + super(type_or_count, count) + + # See https://github.com/ffi/ffi/issues/118 + ffi_type = FFI.find_type(self.type) + + name, _ = FFI::TypeDefs.find do |(name, t)| + t == ffi_type + end + + unless name + raise ArgumentError, "could not find FFI::Type for #{self.type}" + end + + getter = "get_#{name}" + setter = "put_#{name}" + + # singleton class + sclass = class << self; self; end + + unless sclass.method_defined?(getter) + raise ArgumentError, "no element getter for #{self.type} (#{getter})" + end + + unless sclass.method_defined?(setter) + raise ArgumentError, "no element setter for #{self.type} (#{setter})" + end + + sclass.send(:alias_method, :get_type, getter) + sclass.send(:alias_method, :put_type, setter) + end + + def each + # NOTE: using @count because Enumerable defines its own count + # method... + @count.times { |i| yield self[i] } + end + + def each_with_index + @count.times { |i| yield self[i], i } + end + + def [](i) + get_type(i * self.type_size) + end + + def []=(i, val) + put_type(i * self.type_size, val) + end + end +end diff --git a/spec/process_shared/shared_array_spec.rb b/spec/process_shared/shared_array_spec.rb new file mode 100644 index 0000000..f31ec2a --- /dev/null +++ b/spec/process_shared/shared_array_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' +require 'process_shared/shared_array' + +module ProcessShared + describe SharedArray do + it 'initializes arrays' do + mem = SharedArray.new(:int, 10) + 10.times do |i| + mem[i] = i + end + 10.times do |i| + mem[i].must_equal i + end + end + + it 'responds to Enumerable methods' do + mem = SharedArray.new(:int, 4) + 4.times do |i| + mem[i] = i+1 + end + + mem.map { |i| i * 2 }.must_equal [2, 4, 6, 8] + mem.sort.must_equal [1, 2, 3, 4] + end + end +end