[前][次][番号順一覧][スレッド一覧]

ruby-changes:36132

From: knu <ko1@a...>
Date: Fri, 31 Oct 2014 22:22:08 +0900 (JST)
Subject: [ruby-changes:36132] knu:r48213 (trunk): Make Digest() thread-safe.

knu	2014-10-31 22:21:51 +0900 (Fri, 31 Oct 2014)

  New Revision: 48213

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=48213

  Log:
    Make Digest() thread-safe.
    
    * ext/digest/lib/digest.rb (Digest()): This function should now be
      thread-safe.  If you have a problem with regard to on-demand
      loading under a multi-threaded environment, preload "digest/*"
      modules on boot or use this method instead of directly
      referencing Digest::*. [Bug #9494]
      cf. https://github.com/aws/aws-sdk-ruby/issues/525

  Added directories:
    trunk/test/digest/digest/
  Added files:
    trunk/test/digest/digest/foo.rb
  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/ext/digest/lib/digest.rb
    trunk/test/digest/test_digest.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 48212)
+++ ChangeLog	(revision 48213)
@@ -1,3 +1,12 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Fri Oct 31 22:19:30 2014  Akinori MUSHA  <knu@i...>
+
+	* ext/digest/lib/digest.rb (Digest()): This function should now be
+	  thread-safe.  If you have a problem with regard to on-demand
+	  loading under a multi-threaded environment, preload "digest/*"
+	  modules on boot or use this method instead of directly
+	  referencing Digest::*. [Bug #9494]
+	  cf. https://github.com/aws/aws-sdk-ruby/issues/525
+
 Fri Oct 31 21:33:17 2014  Akinori MUSHA  <knu@i...>
 
 	* test/digest/test_digest.rb: Drop #!.  This no longer runs
Index: ext/digest/lib/digest.rb
===================================================================
--- ext/digest/lib/digest.rb	(revision 48212)
+++ ext/digest/lib/digest.rb	(revision 48213)
@@ -1,6 +1,9 @@ https://github.com/ruby/ruby/blob/trunk/ext/digest/lib/digest.rb#L1
 require 'digest.so'
 
 module Digest
+  # A mutex for Digest().
+  REQUIRE_MUTEX = Mutex.new
+
   def self.const_missing(name) # :nodoc:
     case name
     when :SHA256, :SHA384, :SHA512
@@ -76,15 +79,30 @@ end https://github.com/ruby/ruby/blob/trunk/ext/digest/lib/digest.rb#L79
 # call-seq:
 #   Digest(name) -> digest_subclass
 #
-# Returns a Digest subclass by +name+.
+# Returns a Digest subclass by +name+ in a thread-safe manner even
+# when on-demand loading is involved.
 #
 #   require 'digest'
 #
 #   Digest("MD5")
 #   # => Digest::MD5
 #
-#   Digest("Foo")
+#   Digest(:SHA256)
+#   # => Digest::SHA256
+#
+#   Digest(:Foo)
 #   # => LoadError: library not found for class Digest::Foo -- digest/foo
 def Digest(name)
-  Digest.const_get(name)
+  const = name.to_sym
+  Digest::REQUIRE_MUTEX.synchronize {
+    # Ignore autoload's because it is void when we have #const_missing
+    Digest.const_missing(const)
+  }
+rescue LoadError
+  # Constants do not necessarily rely on digest/*.
+  if Digest.const_defined?(const)
+    Digest.const_get(const)
+  else
+    raise
+  end
 end
Index: NEWS
===================================================================
--- NEWS	(revision 48212)
+++ NEWS	(revision 48213)
@@ -128,6 +128,11 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L128
 === Stdlib updates (outstanding ones only)
 
 * Digest
+
+  * Digest() should now be thread-safe.  If you have a problem with
+    regard to on-demand loading under a multi-threaded environment,
+    preload "digest/*" modules on boot or use this method instead of
+    directly referencing Digest::*.
   * Digest::HMAC has been removed just as previously noticed.
 
 * Etc
Index: test/digest/digest/foo.rb
===================================================================
--- test/digest/digest/foo.rb	(revision 0)
+++ test/digest/digest/foo.rb	(revision 48213)
@@ -0,0 +1,10 @@ https://github.com/ruby/ruby/blob/trunk/test/digest/digest/foo.rb#L1
+module Digest
+  Foo = nil
+
+  sleep 0.2
+
+  remove_const(:Foo)
+
+  class Foo < Class
+  end
+end

Property changes on: test/digest/digest/foo.rb
___________________________________________________________________
Added: svn:eol-style
   + LF

Index: test/digest/test_digest.rb
===================================================================
--- test/digest/test_digest.rb	(revision 48212)
+++ test/digest/test_digest.rb	(revision 48213)
@@ -208,4 +208,65 @@ module TestDigest https://github.com/ruby/ruby/blob/trunk/test/digest/test_digest.rb#L208
       end
     end
   end
+
+  class TestDigestParen < Test::Unit::TestCase
+    def test_sha2
+      assert_separately(%w[-rdigest], <<-'end;')
+        assert_nothing_raised {
+          Digest(:SHA256).new
+          Digest(:SHA384).new
+          Digest(:SHA512).new
+        }
+      end;
+    end
+
+    def test_no_lib
+      assert_separately(%w[-rdigest], <<-'end;')
+        class Digest::Nolib < Digest::Class
+        end
+
+        assert_nothing_raised {
+          Digest(:Nolib).new
+        }
+      end;
+    end
+
+    def test_no_lib_no_def
+      assert_separately(%w[-rdigest], <<-'end;')
+        assert_raise(LoadError) {
+          Digest(:Nodef).new
+        }
+      end;
+    end
+
+    def test_race
+      assert_separately(['-rdigest', "-I#{File.dirname(__FILE__)}"], <<-'end;')
+        assert_nothing_raised {
+          t = Thread.start {
+            sleep 0.1
+            Digest(:Foo).new
+          }
+          Digest(:Foo).new
+          t.join
+        }
+      end;
+    end
+
+    def test_race_mixed
+      assert_separately(['-rdigest', "-I#{File.dirname(__FILE__)}"], <<-'end;')
+        assert_nothing_raised {
+          t = Thread.start {
+            sleep 0.1
+            Digest::Foo.new
+          }
+          Digest(:Foo).new
+          begin
+            t.join
+          rescue NoMethodError, NameError
+            # NoMethodError is highly likely; NameError is listed just in case
+          end
+        }
+      end;
+    end
+  end
 end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]