diff options
author | Koichi Sasada <[email protected]> | 2020-11-26 04:25:42 +0900 |
---|---|---|
committer | Koichi Sasada <[email protected]> | 2020-11-27 17:03:30 +0900 |
commit | 2db2fb9f6c742d5bd0019ccd11c7a375e1b12c0b (patch) | |
tree | e5f464004a386e2838a700ad5cced540e8f9953c | |
parent | 8ce1711c255679d38b6a2405ff694eb5b5b2eae5 (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.c | 16 | ||||
-rw-r--r-- | ractor_core.h | 2 | ||||
-rw-r--r-- | random.c | 69 | ||||
-rw-r--r-- | spec/ruby/core/random/default_spec.rb | 17 | ||||
-rw-r--r-- | test/ruby/test_rand.rb | 12 |
5 files changed, 65 insertions, 51 deletions
@@ -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; @@ -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) { |