From 74b33296f23b3494008d57634c270a97ef117b76 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Tue, 20 Apr 2021 16:43:33 -0400 Subject: [PATCH] fix: make Concurrent::Set thread-safe on CRuby Closes #900 --- lib/concurrent-ruby/concurrent/set.rb | 10 +++++++--- .../thread_safe/util/data_structures.rb | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/concurrent-ruby/concurrent/set.rb b/lib/concurrent-ruby/concurrent/set.rb index 602d49408..34618f026 100644 --- a/lib/concurrent-ruby/concurrent/set.rb +++ b/lib/concurrent-ruby/concurrent/set.rb @@ -23,9 +23,13 @@ module Concurrent # @!macro internal_implementation_note SetImplementation = case when Concurrent.on_cruby? - # Because MRI never runs code in parallel, the existing - # non-thread-safe structures should usually work fine. - ::Set + require 'monitor' + require 'concurrent/thread_safe/util/data_structures' + + class CRubySet < ::Set + end + ThreadSafe::Util.make_synchronized_on_cruby Concurrent::CRubySet + CRubySet when Concurrent.on_jruby? require 'jruby/synchronized' diff --git a/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb b/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb index ff1e8ed97..9c8096e8b 100644 --- a/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +++ b/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb @@ -12,6 +12,25 @@ def self.synchronized(object, &block) module Concurrent module ThreadSafe module Util + def self.make_synchronized_on_cruby(klass) + klass.class_eval do + def initialize(*args, &block) + @_monitor = Monitor.new + super + end + end + + klass.superclass.instance_methods(false).each do |method| + klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{method}(*args) + monitor = @_monitor + 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 + def self.make_synchronized_on_rbx(klass) klass.class_eval do private