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:
parent
8ce1711c25
commit
2db2fb9f6c
Notes:
git
2020-11-27 17:03:56 +09:00
5 changed files with 65 additions and 51 deletions
16
ractor.c
16
ractor.c
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
69
random.c
69
random.c
|
@ -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);
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue