mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Autoload encodings on the main ractor
None of the datastructures involved in the require process are safe to call on a secondary ractor, however when autoloading encodings, we do so from the current ractor. So all sorts of corruption can happen when using an autoloaded encoding for the first time from a secondary ractor.
This commit is contained in:
parent
002d746418
commit
482f4cad82
5 changed files with 81 additions and 19 deletions
39
encoding.c
39
encoding.c
|
@ -763,36 +763,47 @@ load_encoding(const char *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
enc_autoload_body(struct enc_table *enc_table, rb_encoding *enc)
|
enc_autoload_body(rb_encoding *enc)
|
||||||
{
|
{
|
||||||
rb_encoding *base = enc_table->list[ENC_TO_ENCINDEX(enc)].base;
|
rb_encoding *base;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
|
||||||
|
base = enc_table->list[ENC_TO_ENCINDEX(enc)].base;
|
||||||
|
if (base) {
|
||||||
|
do {
|
||||||
|
if (i >= enc_table->count) {
|
||||||
|
i = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (enc_table->list[i].enc != base && (++i, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == -1) return -1;
|
||||||
|
|
||||||
if (base) {
|
if (base) {
|
||||||
int i = 0;
|
|
||||||
do {
|
|
||||||
if (i >= enc_table->count) return -1;
|
|
||||||
} while (enc_table->list[i].enc != base && (++i, 1));
|
|
||||||
if (rb_enc_autoload_p(base)) {
|
if (rb_enc_autoload_p(base)) {
|
||||||
if (rb_enc_autoload(base) < 0) return -1;
|
if (rb_enc_autoload(base) < 0) return -1;
|
||||||
}
|
}
|
||||||
i = enc->ruby_encoding_index;
|
i = enc->ruby_encoding_index;
|
||||||
enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
|
|
||||||
|
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
|
||||||
|
enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
|
||||||
|
}
|
||||||
|
|
||||||
((rb_raw_encoding *)enc)->ruby_encoding_index = i;
|
((rb_raw_encoding *)enc)->ruby_encoding_index = i;
|
||||||
i &= ENC_INDEX_MASK;
|
i &= ENC_INDEX_MASK;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return -2;
|
return -2;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_enc_autoload(rb_encoding *enc)
|
rb_enc_autoload(rb_encoding *enc)
|
||||||
{
|
{
|
||||||
int i;
|
int i = enc_autoload_body(enc);
|
||||||
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
|
|
||||||
i = enc_autoload_body(enc_table, enc);
|
|
||||||
}
|
|
||||||
if (i == -2) {
|
if (i == -2) {
|
||||||
i = load_encoding(rb_enc_name(enc));
|
i = load_encoding(rb_enc_name(enc));
|
||||||
}
|
}
|
||||||
|
|
8
load.c
8
load.c
|
@ -372,6 +372,8 @@ features_index_add_single(vm_ns_t *vm_ns, const char* str, size_t len, VALUE off
|
||||||
static void
|
static void
|
||||||
features_index_add(vm_ns_t *vm_ns, VALUE feature, VALUE offset)
|
features_index_add(vm_ns_t *vm_ns, VALUE feature, VALUE offset)
|
||||||
{
|
{
|
||||||
|
RUBY_ASSERT(rb_ractor_main_p());
|
||||||
|
|
||||||
const char *feature_str, *feature_end, *ext, *p;
|
const char *feature_str, *feature_end, *ext, *p;
|
||||||
bool rb = false;
|
bool rb = false;
|
||||||
|
|
||||||
|
@ -1523,6 +1525,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
|
||||||
int
|
int
|
||||||
rb_require_internal_silent(VALUE fname)
|
rb_require_internal_silent(VALUE fname)
|
||||||
{
|
{
|
||||||
|
if (!rb_ractor_main_p()) {
|
||||||
|
return NUM2INT(rb_ractor_require(fname, true));
|
||||||
|
}
|
||||||
|
|
||||||
rb_execution_context_t *ec = GET_EC();
|
rb_execution_context_t *ec = GET_EC();
|
||||||
return require_internal(ec, fname, 1, false);
|
return require_internal(ec, fname, 1, false);
|
||||||
}
|
}
|
||||||
|
@ -1559,7 +1565,7 @@ rb_require_string_internal(VALUE fname, bool resurrect)
|
||||||
// main ractor check
|
// main ractor check
|
||||||
if (!rb_ractor_main_p()) {
|
if (!rb_ractor_main_p()) {
|
||||||
if (resurrect) fname = rb_str_resurrect(fname);
|
if (resurrect) fname = rb_str_resurrect(fname);
|
||||||
return rb_ractor_require(fname);
|
return rb_ractor_require(fname, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
|
int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
|
||||||
|
|
30
ractor.c
30
ractor.c
|
@ -2263,6 +2263,8 @@ struct cross_ractor_require {
|
||||||
// autoload
|
// autoload
|
||||||
VALUE module;
|
VALUE module;
|
||||||
ID name;
|
ID name;
|
||||||
|
|
||||||
|
bool silent;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -2294,7 +2296,14 @@ require_body(VALUE data)
|
||||||
|
|
||||||
ID require;
|
ID require;
|
||||||
CONST_ID(require, "require");
|
CONST_ID(require, "require");
|
||||||
crr->result = rb_funcallv(Qnil, require, 1, &crr->feature);
|
|
||||||
|
if (crr->silent) {
|
||||||
|
int rb_require_internal_silent(VALUE fname);
|
||||||
|
crr->result = INT2NUM(rb_require_internal_silent(crr->feature));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
crr->result = rb_funcallv(Qnil, require, 1, &crr->feature);
|
||||||
|
}
|
||||||
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
@ -2338,10 +2347,21 @@ ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
|
||||||
struct cross_ractor_require *crr;
|
struct cross_ractor_require *crr;
|
||||||
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
|
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
|
||||||
|
|
||||||
|
VALUE debug, errinfo;
|
||||||
|
if (crr->silent) {
|
||||||
|
debug = ruby_debug;
|
||||||
|
errinfo = rb_errinfo();
|
||||||
|
}
|
||||||
|
|
||||||
// catch any error
|
// catch any error
|
||||||
rb_rescue2(func, (VALUE)crr,
|
rb_rescue2(func, (VALUE)crr,
|
||||||
require_rescue, (VALUE)crr, rb_eException, 0);
|
require_rescue, (VALUE)crr, rb_eException, 0);
|
||||||
|
|
||||||
|
if (crr->silent) {
|
||||||
|
ruby_debug = debug;
|
||||||
|
rb_set_errinfo(errinfo);
|
||||||
|
}
|
||||||
|
|
||||||
rb_rescue2(require_result_copy_body, (VALUE)crr,
|
rb_rescue2(require_result_copy_body, (VALUE)crr,
|
||||||
require_result_copy_resuce, (VALUE)crr, rb_eException, 0);
|
require_result_copy_resuce, (VALUE)crr, rb_eException, 0);
|
||||||
|
|
||||||
|
@ -2357,8 +2377,11 @@ ractor_require_func(void *crr_obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_ractor_require(VALUE feature)
|
rb_ractor_require(VALUE feature, bool silent)
|
||||||
{
|
{
|
||||||
|
// We're about to block on the main ractor, so if we're holding the global lock we'll deadlock.
|
||||||
|
ASSERT_vm_unlocking();
|
||||||
|
|
||||||
struct cross_ractor_require *crr;
|
struct cross_ractor_require *crr;
|
||||||
VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
|
VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
|
||||||
FL_SET_RAW(crr_obj, RUBY_FL_SHAREABLE);
|
FL_SET_RAW(crr_obj, RUBY_FL_SHAREABLE);
|
||||||
|
@ -2368,6 +2391,7 @@ rb_ractor_require(VALUE feature)
|
||||||
crr->port = ractor_port_new(GET_RACTOR());
|
crr->port = ractor_port_new(GET_RACTOR());
|
||||||
crr->result = Qundef;
|
crr->result = Qundef;
|
||||||
crr->exception = Qundef;
|
crr->exception = Qundef;
|
||||||
|
crr->silent = silent;
|
||||||
|
|
||||||
rb_execution_context_t *ec = GET_EC();
|
rb_execution_context_t *ec = GET_EC();
|
||||||
rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
|
rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
|
||||||
|
@ -2395,7 +2419,7 @@ rb_ractor_require(VALUE feature)
|
||||||
static VALUE
|
static VALUE
|
||||||
ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
|
ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
|
||||||
{
|
{
|
||||||
return rb_ractor_require(feature);
|
return rb_ractor_require(feature, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
|
|
@ -134,7 +134,7 @@ void rb_ractor_terminate_all(void);
|
||||||
bool rb_ractor_main_p_(void);
|
bool rb_ractor_main_p_(void);
|
||||||
void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th);
|
void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th);
|
||||||
void rb_ractor_terminate_atfork(rb_vm_t *vm, rb_ractor_t *th);
|
void rb_ractor_terminate_atfork(rb_vm_t *vm, rb_ractor_t *th);
|
||||||
VALUE rb_ractor_require(VALUE feature);
|
VALUE rb_ractor_require(VALUE feature, bool silent);
|
||||||
VALUE rb_ractor_autoload_load(VALUE space, ID id);
|
VALUE rb_ractor_autoload_load(VALUE space, ID id);
|
||||||
|
|
||||||
VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name);
|
VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name);
|
||||||
|
|
|
@ -136,4 +136,25 @@ class TestEncoding < Test::Unit::TestCase
|
||||||
assert "[Bug #19562]"
|
assert "[Bug #19562]"
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_ractor_lazy_load_encoding_concurrently
|
||||||
|
assert_ractor("#{<<~"begin;"}\n#{<<~'end;'}")
|
||||||
|
begin;
|
||||||
|
rs = []
|
||||||
|
autoload_encodings = Encoding.list.select { |e| e.inspect.include?("(autoload)") }.freeze
|
||||||
|
7.times do
|
||||||
|
rs << Ractor.new(autoload_encodings) do |encodings|
|
||||||
|
str = "abc".dup
|
||||||
|
encodings.each do |enc|
|
||||||
|
str.force_encoding(enc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while rs.any?
|
||||||
|
r, _obj = Ractor.select(*rs)
|
||||||
|
rs.delete(r)
|
||||||
|
end
|
||||||
|
assert rs.empty?
|
||||||
|
end;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue