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
|
||||
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) {
|
||||
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(base) < 0) return -1;
|
||||
}
|
||||
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;
|
||||
i &= ENC_INDEX_MASK;
|
||||
return i;
|
||||
}
|
||||
else {
|
||||
return -2;
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
int
|
||||
rb_enc_autoload(rb_encoding *enc)
|
||||
{
|
||||
int i;
|
||||
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
|
||||
i = enc_autoload_body(enc_table, enc);
|
||||
}
|
||||
int i = enc_autoload_body(enc);
|
||||
if (i == -2) {
|
||||
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
|
||||
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;
|
||||
bool rb = false;
|
||||
|
||||
|
@ -1523,6 +1525,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
|
|||
int
|
||||
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();
|
||||
return require_internal(ec, fname, 1, false);
|
||||
}
|
||||
|
@ -1559,7 +1565,7 @@ rb_require_string_internal(VALUE fname, bool resurrect)
|
|||
// main ractor check
|
||||
if (!rb_ractor_main_p()) {
|
||||
if (resurrect) fname = rb_str_resurrect(fname);
|
||||
return rb_ractor_require(fname);
|
||||
return rb_ractor_require(fname, false);
|
||||
}
|
||||
else {
|
||||
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
|
||||
VALUE module;
|
||||
ID name;
|
||||
|
||||
bool silent;
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -2294,7 +2296,14 @@ require_body(VALUE data)
|
|||
|
||||
ID 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;
|
||||
}
|
||||
|
@ -2338,10 +2347,21 @@ ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
|
|||
struct cross_ractor_require *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
|
||||
rb_rescue2(func, (VALUE)crr,
|
||||
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,
|
||||
require_result_copy_resuce, (VALUE)crr, rb_eException, 0);
|
||||
|
||||
|
@ -2357,8 +2377,11 @@ ractor_require_func(void *crr_obj)
|
|||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
|
@ -2368,6 +2391,7 @@ rb_ractor_require(VALUE feature)
|
|||
crr->port = ractor_port_new(GET_RACTOR());
|
||||
crr->result = Qundef;
|
||||
crr->exception = Qundef;
|
||||
crr->silent = silent;
|
||||
|
||||
rb_execution_context_t *ec = GET_EC();
|
||||
rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
|
||||
|
@ -2395,7 +2419,7 @@ rb_ractor_require(VALUE feature)
|
|||
static VALUE
|
||||
ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
|
||||
{
|
||||
return rb_ractor_require(feature);
|
||||
return rb_ractor_require(feature, false);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
|
|
@ -134,7 +134,7 @@ void rb_ractor_terminate_all(void);
|
|||
bool rb_ractor_main_p_(void);
|
||||
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);
|
||||
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_ensure_shareable(VALUE obj, VALUE name);
|
||||
|
|
|
@ -136,4 +136,25 @@ class TestEncoding < Test::Unit::TestCase
|
|||
assert "[Bug #19562]"
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue