summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2020-11-26 04:25:42 +0900
committerKoichi Sasada <ko1@atdot.net>2020-11-27 17:03:30 +0900
commit2db2fb9f6c742d5bd0019ccd11c7a375e1b12c0b (patch)
treee5f464004a386e2838a700ad5cced540e8f9953c
parent8ce1711c255679d38b6a2405ff694eb5b5b2eae5 (diff)
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]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3813
-rw-r--r--ractor.c16
-rw-r--r--ractor_core.h2
-rw-r--r--random.c69
-rw-r--r--spec/ruby/core/random/default_spec.rb17
-rw-r--r--test/ruby/test_rand.rb12
5 files changed, 65 insertions, 51 deletions
diff --git a/ractor.c b/ractor.c
index 491a1e3f71..a5f22a4724 100644
--- a/ractor.c
+++ b/ractor.c
@@ -222,6 +222,7 @@ ractor_free(void *ptr)
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)
}
}
+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 07528cc69c..dd662141a0 100644
--- a/ractor_core.h
+++ b/ractor_core.h
@@ -127,6 +127,8 @@ struct rb_ractor_struct {
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 d9a347e426..22c54492e6 100644
--- a/random.c
+++ b/random.c
@@ -120,8 +120,6 @@ typedef struct {
#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)
}
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 = {
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 *
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)
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)
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)
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
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)
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
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)
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)
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)
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 1755154294..0333abed6b 100644
--- a/spec/ruby/core/random/default_spec.rb
+++ b/spec/ruby/core/random/default_spec.rb
@@ -1,8 +1,21 @@
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 2fcac2d7d0..af10a27ce0 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -336,18 +336,6 @@ class TestRand < Test::Unit::TestCase
}
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) {