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

ruby-changes:63767

From: Koichi <ko1@a...>
Date: Fri, 27 Nov 2020 17:03:55 +0900 (JST)
Subject: [ruby-changes:63767] 2db2fb9f6c (master): per-ractor Random::DEFAULT

https://git.ruby-lang.org/ruby.git/commit/?id=2db2fb9f6c

From 2db2fb9f6c742d5bd0019ccd11c7a375e1b12c0b Mon Sep 17 00:00:00 2001
From: Koichi Sasada <ko1@a...>
Date: Thu, 26 Nov 2020 04:25:42 +0900
Subject: per-ractor Random::DEFAULT

Random generators are not Ractor-safe, so we need to prepare
per-ractor default random genearators. This patch set
`Random::DEFAULT = Randm` (not a Random instance, but the Random
class) and singleton methods like `Random.rand()` use a per-ractor
random generator.

[Feature #17322]

diff --git a/ractor.c b/ractor.c
index 491a1e3..a5f22a4 100644
--- a/ractor.c
+++ b/ractor.c
@@ -222,6 +222,7 @@ ractor_free(void *ptr) https://github.com/ruby/ruby/blob/trunk/ractor.c#L222
     rb_native_cond_destroy(&r->wait.cond);
     ractor_queue_free(&r->incoming_queue);
     ractor_waiting_list_free(&r->taking_ractors);
+    if (r->default_rand) ruby_xfree(r->default_rand);
     ruby_xfree(r);
 }
 
@@ -1767,6 +1768,21 @@ rb_ractor_stderr_set(VALUE err) https://github.com/ruby/ruby/blob/trunk/ractor.c#L1768
     }
 }
 
+void *
+rb_ractor_default_rand(void *ptr)
+{
+    if (rb_ractor_main_p()) {
+        static void *default_rnd;
+        if (UNLIKELY(ptr != NULL)) default_rnd = ptr;
+        return default_rnd;
+    }
+    else {
+        rb_ractor_t *cr = GET_RACTOR();
+        if (UNLIKELY(ptr != NULL)) cr->default_rand = ptr;
+        return cr->default_rand;
+    }
+}
+
 /// traverse function
 
 // 2: stop search
diff --git a/ractor_core.h b/ractor_core.h
index 07528cc..dd66214 100644
--- a/ractor_core.h
+++ b/ractor_core.h
@@ -127,6 +127,8 @@ struct rb_ractor_struct { https://github.com/ruby/ruby/blob/trunk/ractor_core.h#L127
     VALUE verbose;
     VALUE debug;
 
+    void *default_rand; // used in random.c
+
     // gc.c rb_objspace_reachable_objects_from
     struct gc_mark_func_data_struct {
         void *data;
diff --git a/random.c b/random.c
index d9a347e..22c5449 100644
--- a/random.c
+++ b/random.c
@@ -120,8 +120,6 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/random.c#L120
 
 #define DEFAULT_SEED_CNT 4
 
-static rb_random_mt_t default_rand;
-
 static VALUE rand_init(const rb_random_interface_t *, rb_random_t *, VALUE);
 static VALUE random_seed(VALUE);
 static void fill_random_seed(uint32_t *seed, size_t cnt);
@@ -149,9 +147,21 @@ rand_start(rb_random_mt_t *r) https://github.com/ruby/ruby/blob/trunk/random.c#L147
 }
 
 static rb_random_mt_t *
+default_rand(void)
+{
+    void *rb_ractor_default_rand(void *); // ractor.c
+    rb_random_mt_t *rnd = (rb_random_mt_t *)rb_ractor_default_rand(NULL);
+    if (rnd == NULL) {
+        rnd = ZALLOC(rb_random_mt_t);
+        rb_ractor_default_rand(rnd);
+    }
+    return rnd;
+}
+
+static rb_random_mt_t *
 default_mt(void)
 {
-    return rand_mt_start(&default_rand);
+    return rand_mt_start(default_rand());
 }
 
 unsigned int
@@ -230,7 +240,7 @@ const rb_data_type_t rb_random_data_type = { https://github.com/ruby/ruby/blob/trunk/random.c#L240
 static void
 random_mt_free(void *ptr)
 {
-    if (ptr != &default_rand)
+    if (ptr != default_rand())
 	xfree(ptr);
 }
 
@@ -274,7 +284,7 @@ static rb_random_t * https://github.com/ruby/ruby/blob/trunk/random.c#L284
 try_get_rnd(VALUE obj)
 {
     if (obj == rb_cRandom) {
-	return rand_start(&default_rand);
+	return rand_start(default_rand());
     }
     if (!rb_typeddata_is_kind_of(obj, &rb_random_data_type)) return NULL;
     if (RTYPEDDATA_TYPE(obj) == &random_mt_type)
@@ -290,7 +300,7 @@ try_get_rnd(VALUE obj) https://github.com/ruby/ruby/blob/trunk/random.c#L300
 static const rb_random_interface_t *
 try_rand_if(VALUE obj, rb_random_t *rnd)
 {
-    if (rnd == &default_rand.base) {
+    if (rnd == &default_rand()->base) {
 	return &random_mt_if;
     }
     return rb_rand_if(obj);
@@ -709,7 +719,7 @@ rand_mt_state(VALUE obj) https://github.com/ruby/ruby/blob/trunk/random.c#L719
 static VALUE
 random_s_state(VALUE klass)
 {
-    return mt_state(&default_rand.mt);
+    return mt_state(&default_rand()->mt);
 }
 
 /* :nodoc: */
@@ -724,7 +734,7 @@ rand_mt_left(VALUE obj) https://github.com/ruby/ruby/blob/trunk/random.c#L734
 static VALUE
 random_s_left(VALUE klass)
 {
-    return INT2FIX(default_rand.mt.left);
+    return INT2FIX(default_rand()->mt.left);
 }
 
 /* :nodoc: */
@@ -829,7 +839,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/random.c#L839
 rb_f_srand(int argc, VALUE *argv, VALUE obj)
 {
     VALUE seed, old;
-    rb_random_mt_t *r = &default_rand;
+    rb_random_mt_t *r = rand_mt_start(default_rand());
 
     if (rb_check_arity(argc, 0, 1) == 0) {
         seed = random_seed(obj);
@@ -1191,11 +1201,18 @@ rb_random_bytes(VALUE obj, long n) https://github.com/ruby/ruby/blob/trunk/random.c#L1201
 static VALUE
 random_s_bytes(VALUE obj, VALUE len)
 {
-    rb_random_t *rnd = rand_start(&default_rand);
+    rb_random_t *rnd = rand_start(default_rand());
     return rand_bytes(&random_mt_if, rnd, NUM2LONG(rb_to_int(len)));
 }
 
 static VALUE
+random_s_seed(VALUE obj)
+{
+    rb_random_mt_t *rnd = rand_mt_start(default_rand());
+    return rnd->base.seed;
+}
+
+static VALUE
 range_values(VALUE vmax, VALUE *begp, VALUE *endp, int *exclp)
 {
     VALUE beg, end;
@@ -1518,7 +1535,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/random.c#L1535
 rb_f_rand(int argc, VALUE *argv, VALUE obj)
 {
     VALUE vmax;
-    rb_random_t *rnd = rand_start(&default_rand);
+    rb_random_t *rnd = rand_start(default_rand());
 
     if (rb_check_arity(argc, 0, 1) && !NIL_P(vmax = argv[0])) {
         VALUE v = rand_range(obj, rnd, vmax);
@@ -1543,7 +1560,7 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj) https://github.com/ruby/ruby/blob/trunk/random.c#L1560
 static VALUE
 random_s_rand(int argc, VALUE *argv, VALUE obj)
 {
-    VALUE v = rand_random(argc, argv, Qnil, rand_start(&default_rand));
+    VALUE v = rand_random(argc, argv, Qnil, rand_start(default_rand()));
     check_random_number(v, argv);
     return v;
 }
@@ -1626,27 +1643,10 @@ Init_RandomSeedCore(void) https://github.com/ruby/ruby/blob/trunk/random.c#L1643
     explicit_bzero(&mt, sizeof(mt));
 }
 
-/* construct Random::DEFAULT bits */
-static VALUE
-Init_Random_default(VALUE klass)
-{
-    rb_random_mt_t *r = &default_rand;
-    struct MT *mt = &r->mt;
-    VALUE v = TypedData_Wrap_Struct(klass, &random_mt_type, r);
-
-    rb_gc_register_mark_object(v);
-    with_random_seed(DEFAULT_SEED_CNT, 1) {
-        init_by_array(mt, seedbuf, DEFAULT_SEED_CNT);
-        r->base.seed = make_seed_value(seedbuf, DEFAULT_SEED_CNT);
-    }
-
-    return v;
-}
-
 void
 rb_reset_random_seed(void)
 {
-    rb_random_mt_t *r = &default_rand;
+    rb_random_mt_t *r = default_rand();
     uninit_genrand(&r->mt);
     r->base.seed = INT2FIX(0);
 }
@@ -1701,17 +1701,12 @@ InitVM_Random(void) https://github.com/ruby/ruby/blob/trunk/random.c#L1701
     rb_define_private_method(rb_cRandom, "left", rand_mt_left, 0);
     rb_define_method(rb_cRandom, "==", rand_mt_equal, 1);
 
-    {
-	/* Direct access to Ruby's Pseudorandom number generator (PRNG). */
-        VALUE rand_default = Init_Random_default(rb_cRandom);
-	/* The default Pseudorandom number generator.  Used by class
-	 * methods of Random. */
-	rb_define_const(rb_cRandom, "DEFAULT", rand_default);
-    }
+    rb_define_const(rb_cRandom, "DEFAULT", rb_cRandom);
 
     rb_define_singleton_method(rb_cRandom, "srand", rb_f_srand, -1);
     rb_define_singleton_method(rb_cRandom, "rand", random_s_rand, -1);
     rb_define_singleton_method(rb_cRandom, "bytes", random_s_bytes, 1);
+    rb_define_singleton_method(rb_cRandom, "seed", random_s_seed, 0);
     rb_define_singleton_method(rb_cRandom, "new_seed", random_seed, 0);
     rb_define_singleton_method(rb_cRandom, "urandom", random_raw_seed, 1);
     rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0);
diff --git a/spec/ruby/core/random/default_spec.rb b/spec/ruby/core/random/default_spec.rb
index 1755154..0333abe 100644
--- a/spec/ruby/core/random/default_spec.rb
+++ b/spec/ruby/core/random/default_spec.rb
@@ -1,8 +1,21 @@ https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/random/default_spec.rb#L1
 require_relative '../../spec_helper'
 
 describe "Random::DEFAULT" do
-  it "returns a Random instance" do
-    Random::DEFAULT.should be_an_instance_of(Random)
+
+  it "returns a random number generator" do
+    Random::DEFAULT.should respond_to(:rand)
+  end
+
+  ruby_version_is ''...'3.0' do
+    it "returns a Random instance" do
+      Random::DEFAULT.should be_an_instance_of(Random)
+    end
+  end
+
+  ruby_version_is '3.0' do
+    it "returns a Random instance" do
+      Random::DEFAULT.should be_an_instance_of(Class)
+    end
   end
 
   it "changes seed on reboot" do
diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb
index 2fcac2d..af10a27 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -336,18 +336,6 @@ class TestRand < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_rand.rb#L336
     }
   end
 
-  def test_default
-    r1 = Random::DEFAULT.dup
-    r2 = Random::DEFAULT.dup
-    3.times do
-      x0 = rand
-      x1 = r1.rand
-      x2 = r2.rand
-      assert_equal(x0, x1)
-      assert_equal(x0, x2)
-    end
-  end
-
   def test_marshal
     bug3656 = '[ruby-core:31622]'
     assert_raise(TypeError, bug3656) {
-- 
cgit v0.10.2


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

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