Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add SharedArray extension of SharedMemory (with Enumerable mixin).

  • Loading branch information...
commit 611c41f9393b625ed2badd316a882c3421e8a701 1 parent ea06367
@pmahoney authored
View
68 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
View
26 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

3 comments on commit 611c41f

@rdp

do you think it should start with default values, or respond to something like
a = SharedArray.new(:max => 30)
a << 3 # remembers its size?

@pmahoney
Owner

Good question. Probably I should emulate Array#new. Then on top of that, I could add a :max or :capacity option to support growable array (not beyond the capacity). Maybe :max is better since in, e.g. Java, capacity refers to the initial capacity; ArrayList can grow beyond that if necessary.

@rdp
Please sign in to comment.
Something went wrong with that request. Please try again.