diff --git a/lib/concurrent/array.rb b/lib/concurrent/array.rb index c83f3bb06..f3786056e 100644 --- a/lib/concurrent/array.rb +++ b/lib/concurrent/array.rb @@ -2,7 +2,8 @@ require 'concurrent/thread_safe/util' module Concurrent - if Concurrent.on_cruby? + case + when Concurrent.on_cruby? # Because MRI never runs code in parallel, the existing # non-thread-safe structures should usually work fine. @@ -21,10 +22,10 @@ module Concurrent # may be lost. Use `#concat` instead. # # @see http://ruby-doc.org/core-2.2.0/Array.html Ruby standard library `Array` - class Array < ::Array; + class Array < ::Array end - elsif Concurrent.on_jruby? + when Concurrent.on_jruby? require 'jruby/synchronized' # @!macro concurrent_array @@ -32,15 +33,29 @@ class Array < ::Array include JRuby::Synchronized end - elsif Concurrent.on_rbx? || Concurrent.on_truffleruby? + when Concurrent.on_rbx? require 'monitor' - require 'concurrent/thread_safe/util/array_hash_rbx' + require 'concurrent/thread_safe/util/data_structures' # @!macro concurrent_array class Array < ::Array end ThreadSafe::Util.make_synchronized_on_rbx Concurrent::Array + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + # @!macro concurrent_array + class Array < ::Array + end + + ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::Array + + else + warn 'Possibly unsupported Ruby implementation' + class Array < ::Array + end end end diff --git a/lib/concurrent/hash.rb b/lib/concurrent/hash.rb index 9c952c1fb..3ecc2ff04 100644 --- a/lib/concurrent/hash.rb +++ b/lib/concurrent/hash.rb @@ -2,7 +2,8 @@ require 'concurrent/thread_safe/util' module Concurrent - if Concurrent.on_cruby? + case + when Concurrent.on_cruby? # @!macro [attach] concurrent_hash # @@ -12,10 +13,10 @@ module Concurrent # which takes the lock repeatedly when reading an item. # # @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash` - class Hash < ::Hash; + class Hash < ::Hash end - elsif Concurrent.on_jruby? + when Concurrent.on_jruby? require 'jruby/synchronized' # @!macro concurrent_hash @@ -23,14 +24,30 @@ class Hash < ::Hash include JRuby::Synchronized end - elsif Concurrent.on_rbx? || Concurrent.on_truffleruby? + when Concurrent.on_rbx? require 'monitor' - require 'concurrent/thread_safe/util/array_hash_rbx' + require 'concurrent/thread_safe/util/data_structures' # @!macro concurrent_hash class Hash < ::Hash end ThreadSafe::Util.make_synchronized_on_rbx Concurrent::Hash + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + # @!macro concurrent_hash + class Hash < ::Hash + end + + ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::Hash + + else + warn 'Possibly unsupported Ruby implementation' + class Hash < ::Hash + end + end end + diff --git a/lib/concurrent/set.rb b/lib/concurrent/set.rb index 7e9956f29..8e72d9bb4 100644 --- a/lib/concurrent/set.rb +++ b/lib/concurrent/set.rb @@ -3,7 +3,8 @@ require 'set' module Concurrent - if Concurrent.on_cruby? + case + when Concurrent.on_cruby? # Because MRI never runs code in parallel, the existing # non-thread-safe structures should usually work fine. @@ -25,7 +26,7 @@ module Concurrent class Set < ::Set; end - elsif Concurrent.on_jruby? + when Concurrent.on_jruby? require 'jruby/synchronized' # @!macro concurrent_Set @@ -33,15 +34,29 @@ class Set < ::Set include JRuby::Synchronized end - elsif Concurrent.on_rbx? || Concurrent.on_truffleruby? + when Concurrent.on_rbx? require 'monitor' - require 'concurrent/thread_safe/util/array_hash_rbx' + require 'concurrent/thread_safe/util/data_structures' # @!macro concurrent_Set class Set < ::Set end - ThreadSafe::Util.make_synchronized_on_rbx Set + ThreadSafe::Util.make_synchronized_on_rbx Concurrent::Set + + when Concurrent.on_truffleruby? + require 'concurrent/thread_safe/util/data_structures' + + # @!macro concurrent_array + class Set < ::Set + end + + ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::Set + + else + warn 'Possibly unsupported Ruby implementation' + class Set < ::Set + end end end diff --git a/lib/concurrent/thread_safe/util/array_hash_rbx.rb b/lib/concurrent/thread_safe/util/data_structures.rb similarity index 61% rename from lib/concurrent/thread_safe/util/array_hash_rbx.rb rename to lib/concurrent/thread_safe/util/data_structures.rb index ee129efdf..e43d9c50a 100644 --- a/lib/concurrent/thread_safe/util/array_hash_rbx.rb +++ b/lib/concurrent/thread_safe/util/data_structures.rb @@ -6,23 +6,13 @@ module Util def self.make_synchronized_on_rbx(klass) klass.class_eval do private + def _mon_initialize @_monitor = Monitor.new unless @_monitor # avoid double initialisation end - def initialize(*args) - _mon_initialize - super - end - - def self.allocate - obj = super - obj.send(:_mon_initialize) - obj - end - - def self.[](*args) - obj = super + def self.new(*args) + obj = super(*args) obj.send(:_mon_initialize) obj end @@ -42,18 +32,24 @@ def #{method}(*args) klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(*args) monitor = @_monitor - - unless monitor - raise("BUG: Internal monitor was not properly initialized. Please report this to the "\ - "concurrent-ruby developers.") - end - + monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.") monitor.synchronize { super } end RUBY end end end + + def self.make_synchronized_on_truffleruby(klass) + klass.superclass.instance_methods(false).each do |method| + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args, &block) + # TODO (pitr-ch 01-Jul-2018): don't use internal TruffleRuby APIs + Truffle::System.synchronized(self) { super(*args, &block) } + end + RUBY + end + end end end end diff --git a/spec/concurrent/array_spec.rb b/spec/concurrent/array_spec.rb index c0ecab38f..f79f8e077 100644 --- a/spec/concurrent/array_spec.rb +++ b/spec/concurrent/array_spec.rb @@ -68,7 +68,7 @@ module Concurrent context 'concurrency' do it do (1..Concurrent::ThreadSafe::Test::THREADS).map do |i| - in_thread do + in_thread(ary) do |ary| 1000.times do ary << i ary.each { |x| x * 2 } @@ -82,7 +82,7 @@ module Concurrent end describe '#slice' do - # This is mostly relevant on Rubinius and Truffle + # This is mostly relevant on Rubinius and TruffleRuby it 'correctly initializes the monitor' do ary.concat([0, 1, 2, 3, 4, 5, 6, 7, 8])