-
Notifications
You must be signed in to change notification settings - Fork 5.4k
per-ractor Random::DEFAULT #3813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -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()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default_mt()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we call rand_mt_start
here, will rand_init
be called twice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without it, the previous seed will be false
(Qfalse
) and srand() returns false
if there is no usages of default_rand
like rand()
and so on.
If returning false
by srand()
, we can delete it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since rand_init()
should initialize r->mt
, genrand_initialized
should be true and it will not be called again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default_rand
allocates the memory of the default random object at the first srand
call in each ractor.
Then random_mt_start
calls rand_init
for the default random object.
The second rand_init
is called below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see.
Seems OK to return 0
when old == Qfalse
, instead of initialization here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll fix it.
static VALUE | ||
random_s_seed(VALUE obj) | ||
{ | ||
rb_random_mt_t *rnd = rand_mt_start(default_rand()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def test_default_seed
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
seed = Random::DEFAULT::seed
rand1 = Random::DEFAULT::rand
rand2 = Random.new(seed).rand
assert_equal(rand1, rand2)
srand seed
rand3 = rand
assert_equal(rand1, rand3)
end;
end
This test doesn't work...
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | |
it "returns a random number generator" do | |
Random::DEFAULT.should respond_to(:rand) | |
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems nice, but OTOH the current diff has the advantage that other Ruby implementations will notice the change and that they need to change what Random::DEFAULT points it when updating to Ruby 3.
Maybe have both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be anything which generates (pseudo) random numbers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't feel reasonable that "the PRNG should be a class", but rather nonsense.
"It should respond to rand
" would be fine as duck-typing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is an implementation detail which is useless to be shared.
In fact, the constant might be a proxy object, and could be changed.
What we consider is just that "it works as a RNG".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Till now, no code uses that "Random::DEFAULT equals Random", of course.
If we publish this statement, users may write such code and we (including other implementors) will have to accept it.
If we don't, we can reject such code.
This is chicken-and-egg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because Random::DEFAULT
is Random
will be in 3.0, users might depend on it. It would be nicer if they don't , but they might and there is nothing we can do about it, except if we deprecate the Random::DEFAULT constant.
The fact there is a test in ruby/spec doesn't mean some code is supported, recommended or anything. It just tries to test the behavior, in as much details as possible to improve compatibility between Ruby implementations.
Probably lots of tests in ruby/spec are not what user code should do, but they are useful tests nevertheless.
BTW, I think we should mention this change in NEWS
because it could cause some incompatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact there is a test in ruby/spec doesn't mean some code is supported, recommended or anything. It just tries to test the behavior, in as much details as possible to improve compatibility between Ruby implementations.
Probably lots of tests in ruby/spec are not what user code should do, but they are useful tests nevertheless.
Probably I've misunderstood your intention, ruby/spec is only for other implementors, but the other users should not rely on it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like I forgot to reply to this.
I think ruby/spec is primarily for Ruby implementors yes, similar to the CRuby test suite.
I don't think many Ruby users look at ruby/spec, although of course they enjoy better-tested Ruby implementations.
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]
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 Randomclass) and singleton methods like
Random.rand()
use a per-ractorrandom generator.