1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

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]
This commit is contained in:
Koichi Sasada 2020-11-26 04:25:42 +09:00
parent 8ce1711c25
commit 2db2fb9f6c
Notes: git 2020-11-27 17:03:56 +09:00
5 changed files with 65 additions and 51 deletions

View file

@ -222,6 +222,7 @@ ractor_free(void *ptr)
rb_native_cond_destroy(&r->wait.cond); rb_native_cond_destroy(&r->wait.cond);
ractor_queue_free(&r->incoming_queue); ractor_queue_free(&r->incoming_queue);
ractor_waiting_list_free(&r->taking_ractors); ractor_waiting_list_free(&r->taking_ractors);
if (r->default_rand) ruby_xfree(r->default_rand);
ruby_xfree(r); 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 /// traverse function
// 2: stop search // 2: stop search

View file

@ -127,6 +127,8 @@ struct rb_ractor_struct {
VALUE verbose; VALUE verbose;
VALUE debug; VALUE debug;
void *default_rand; // used in random.c
// gc.c rb_objspace_reachable_objects_from // gc.c rb_objspace_reachable_objects_from
struct gc_mark_func_data_struct { struct gc_mark_func_data_struct {
void *data; void *data;

View file

@ -120,8 +120,6 @@ typedef struct {
#define DEFAULT_SEED_CNT 4 #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 rand_init(const rb_random_interface_t *, rb_random_t *, VALUE);
static VALUE random_seed(VALUE); static VALUE random_seed(VALUE);
static void fill_random_seed(uint32_t *seed, size_t cnt); static void fill_random_seed(uint32_t *seed, size_t cnt);
@ -148,10 +146,22 @@ rand_start(rb_random_mt_t *r)
return &rand_mt_start(r)->base; return &rand_mt_start(r)->base;
} }
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 * static rb_random_mt_t *
default_mt(void) default_mt(void)
{ {
return rand_mt_start(&default_rand); return rand_mt_start(default_rand());
} }
unsigned int unsigned int
@ -230,7 +240,7 @@ const rb_data_type_t rb_random_data_type = {
static void static void
random_mt_free(void *ptr) random_mt_free(void *ptr)
{ {
if (ptr != &default_rand) if (ptr != default_rand())
xfree(ptr); xfree(ptr);
} }
@ -274,7 +284,7 @@ static rb_random_t *
try_get_rnd(VALUE obj) try_get_rnd(VALUE obj)
{ {
if (obj == rb_cRandom) { 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 (!rb_typeddata_is_kind_of(obj, &rb_random_data_type)) return NULL;
if (RTYPEDDATA_TYPE(obj) == &random_mt_type) if (RTYPEDDATA_TYPE(obj) == &random_mt_type)
@ -290,7 +300,7 @@ try_get_rnd(VALUE obj)
static const rb_random_interface_t * static const rb_random_interface_t *
try_rand_if(VALUE obj, rb_random_t *rnd) try_rand_if(VALUE obj, rb_random_t *rnd)
{ {
if (rnd == &default_rand.base) { if (rnd == &default_rand()->base) {
return &random_mt_if; return &random_mt_if;
} }
return rb_rand_if(obj); return rb_rand_if(obj);
@ -709,7 +719,7 @@ rand_mt_state(VALUE obj)
static VALUE static VALUE
random_s_state(VALUE klass) random_s_state(VALUE klass)
{ {
return mt_state(&default_rand.mt); return mt_state(&default_rand()->mt);
} }
/* :nodoc: */ /* :nodoc: */
@ -724,7 +734,7 @@ rand_mt_left(VALUE obj)
static VALUE static VALUE
random_s_left(VALUE klass) random_s_left(VALUE klass)
{ {
return INT2FIX(default_rand.mt.left); return INT2FIX(default_rand()->mt.left);
} }
/* :nodoc: */ /* :nodoc: */
@ -829,7 +839,7 @@ static VALUE
rb_f_srand(int argc, VALUE *argv, VALUE obj) rb_f_srand(int argc, VALUE *argv, VALUE obj)
{ {
VALUE seed, old; 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) { if (rb_check_arity(argc, 0, 1) == 0) {
seed = random_seed(obj); seed = random_seed(obj);
@ -1191,10 +1201,17 @@ rb_random_bytes(VALUE obj, long n)
static VALUE static VALUE
random_s_bytes(VALUE obj, VALUE len) 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))); 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 static VALUE
range_values(VALUE vmax, VALUE *begp, VALUE *endp, int *exclp) range_values(VALUE vmax, VALUE *begp, VALUE *endp, int *exclp)
{ {
@ -1518,7 +1535,7 @@ static VALUE
rb_f_rand(int argc, VALUE *argv, VALUE obj) rb_f_rand(int argc, VALUE *argv, VALUE obj)
{ {
VALUE vmax; 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])) { if (rb_check_arity(argc, 0, 1) && !NIL_P(vmax = argv[0])) {
VALUE v = rand_range(obj, rnd, vmax); VALUE v = rand_range(obj, rnd, vmax);
@ -1543,7 +1560,7 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj)
static VALUE static VALUE
random_s_rand(int argc, VALUE *argv, VALUE obj) 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); check_random_number(v, argv);
return v; return v;
} }
@ -1626,27 +1643,10 @@ Init_RandomSeedCore(void)
explicit_bzero(&mt, sizeof(mt)); 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 void
rb_reset_random_seed(void) rb_reset_random_seed(void)
{ {
rb_random_mt_t *r = &default_rand; rb_random_mt_t *r = default_rand();
uninit_genrand(&r->mt); uninit_genrand(&r->mt);
r->base.seed = INT2FIX(0); 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_private_method(rb_cRandom, "left", rand_mt_left, 0);
rb_define_method(rb_cRandom, "==", rand_mt_equal, 1); rb_define_method(rb_cRandom, "==", rand_mt_equal, 1);
{ rb_define_const(rb_cRandom, "DEFAULT", rb_cRandom);
/* 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_singleton_method(rb_cRandom, "srand", rb_f_srand, -1); 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, "rand", random_s_rand, -1);
rb_define_singleton_method(rb_cRandom, "bytes", random_s_bytes, 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, "new_seed", random_seed, 0);
rb_define_singleton_method(rb_cRandom, "urandom", random_raw_seed, 1); rb_define_singleton_method(rb_cRandom, "urandom", random_raw_seed, 1);
rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0); rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0);

View file

@ -1,9 +1,22 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "Random::DEFAULT" do describe "Random::DEFAULT" do
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 it "returns a Random instance" do
Random::DEFAULT.should be_an_instance_of(Random) Random::DEFAULT.should be_an_instance_of(Random)
end 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 it "changes seed on reboot" do
seed1 = ruby_exe('p Random::DEFAULT.seed', options: '--disable-gems') seed1 = ruby_exe('p Random::DEFAULT.seed', options: '--disable-gems')

View file

@ -336,18 +336,6 @@ class TestRand < Test::Unit::TestCase
} }
end 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 def test_marshal
bug3656 = '[ruby-core:31622]' bug3656 = '[ruby-core:31622]'
assert_raise(TypeError, bug3656) { assert_raise(TypeError, bug3656) {