Skip to content

Commit bbc31b9

Browse files
committed
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.
1 parent 6bb2b26 commit bbc31b9

4 files changed

Lines changed: 65 additions & 48 deletions

File tree

encoding.c

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -725,38 +725,12 @@ rb_enc_get_from_index(int index)
725725

726726
int rb_require_internal_silent(VALUE fname);
727727

728-
static VALUE
729-
require_silent_safe(VALUE fname)
730-
{
731-
return INT2NUM(rb_require_internal_silent(fname));
732-
}
733-
734-
static int
735-
require_silent_sync(VALUE fname)
736-
{
737-
int state;
738-
VALUE retval;
739-
RB_VM_LOCKING() {
740-
VALUE debug = ruby_debug;
741-
VALUE errinfo = rb_errinfo();
742-
743-
ruby_debug = Qfalse;
744-
745-
retval = rb_protect(require_silent_safe, fname, &state);
746-
747-
ruby_debug = debug;
748-
rb_set_errinfo(errinfo);
749-
}
750-
if (state) {
751-
rb_jump_tag(state);
752-
}
753-
return NUM2INT(retval);
754-
}
755-
756728
static int
757729
load_encoding(const char *name)
758730
{
759731
VALUE enclib = rb_sprintf("enc/%s.so", name);
732+
VALUE debug = ruby_debug;
733+
VALUE errinfo;
760734
char *s = RSTRING_PTR(enclib) + 4, *e = RSTRING_END(enclib) - 3;
761735
int loaded;
762736
int idx;
@@ -767,7 +741,11 @@ load_encoding(const char *name)
767741
++s;
768742
}
769743
enclib = rb_fstring(enclib);
770-
loaded = require_silent_sync(enclib);
744+
ruby_debug = Qfalse;
745+
errinfo = rb_errinfo();
746+
loaded = rb_require_internal_silent(enclib);
747+
ruby_debug = debug;
748+
rb_set_errinfo(errinfo);
771749

772750
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
773751
if (loaded < 0 || 1 < loaded) {
@@ -785,36 +763,47 @@ load_encoding(const char *name)
785763
}
786764

787765
static int
788-
enc_autoload_body(struct enc_table *enc_table, rb_encoding *enc)
766+
enc_autoload_body(rb_encoding *enc)
789767
{
790-
rb_encoding *base = enc_table->list[ENC_TO_ENCINDEX(enc)].base;
768+
rb_encoding *base;
769+
int i = 0;
770+
771+
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
772+
base = enc_table->list[ENC_TO_ENCINDEX(enc)].base;
773+
if (base) {
774+
do {
775+
if (i >= enc_table->count) {
776+
i = -1;
777+
break;
778+
}
779+
} while (enc_table->list[i].enc != base && (++i, 1));
780+
}
781+
}
782+
783+
if (i == -1) return -1;
791784

792785
if (base) {
793-
int i = 0;
794-
do {
795-
if (i >= enc_table->count) return -1;
796-
} while (enc_table->list[i].enc != base && (++i, 1));
797786
if (rb_enc_autoload_p(base)) {
798787
if (rb_enc_autoload(base) < 0) return -1;
799788
}
800789
i = enc->ruby_encoding_index;
801-
enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
790+
791+
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
792+
enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
793+
}
794+
802795
((rb_raw_encoding *)enc)->ruby_encoding_index = i;
803796
i &= ENC_INDEX_MASK;
804797
return i;
805798
}
806-
else {
807-
return -2;
808-
}
799+
800+
return -2;
809801
}
810802

811803
int
812804
rb_enc_autoload(rb_encoding *enc)
813805
{
814-
int i;
815-
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
816-
i = enc_autoload_body(enc_table, enc);
817-
}
806+
int i = enc_autoload_body(enc);
818807
if (i == -2) {
819808
i = load_encoding(rb_enc_name(enc));
820809
}

load.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,10 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool wa
15251525
int
15261526
rb_require_internal_silent(VALUE fname)
15271527
{
1528+
if (!rb_ractor_main_p()) {
1529+
return NUM2INT(rb_ractor_require(fname, true));
1530+
}
1531+
15281532
rb_execution_context_t *ec = GET_EC();
15291533
return require_internal(ec, fname, 1, false);
15301534
}
@@ -1561,7 +1565,7 @@ rb_require_string_internal(VALUE fname, bool resurrect)
15611565
// main ractor check
15621566
if (!rb_ractor_main_p()) {
15631567
if (resurrect) fname = rb_str_resurrect(fname);
1564-
return rb_ractor_require(fname);
1568+
return rb_ractor_require(fname, false);
15651569
}
15661570
else {
15671571
int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));

ractor.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,8 @@ struct cross_ractor_require {
22632263
// autoload
22642264
VALUE module;
22652265
ID name;
2266+
2267+
bool silent;
22662268
};
22672269

22682270
static void
@@ -2294,7 +2296,14 @@ require_body(VALUE data)
22942296

22952297
ID require;
22962298
CONST_ID(require, "require");
2297-
crr->result = rb_funcallv(Qnil, require, 1, &crr->feature);
2299+
2300+
if (crr->silent) {
2301+
int rb_require_internal_silent(VALUE fname);
2302+
crr->result = INT2NUM(rb_require_internal_silent(crr->feature));
2303+
}
2304+
else {
2305+
crr->result = rb_funcallv(Qnil, require, 1, &crr->feature);
2306+
}
22982307

22992308
return Qnil;
23002309
}
@@ -2338,10 +2347,21 @@ ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
23382347
struct cross_ractor_require *crr;
23392348
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
23402349

2350+
VALUE debug, errinfo;
2351+
if (crr->silent) {
2352+
debug = ruby_debug;
2353+
errinfo = rb_errinfo();
2354+
}
2355+
23412356
// catch any error
23422357
rb_rescue2(func, (VALUE)crr,
23432358
require_rescue, (VALUE)crr, rb_eException, 0);
23442359

2360+
if (crr->silent) {
2361+
ruby_debug = debug;
2362+
rb_set_errinfo(errinfo);
2363+
}
2364+
23452365
rb_rescue2(require_result_copy_body, (VALUE)crr,
23462366
require_result_copy_resuce, (VALUE)crr, rb_eException, 0);
23472367

@@ -2357,8 +2377,11 @@ ractor_require_func(void *crr_obj)
23572377
}
23582378

23592379
VALUE
2360-
rb_ractor_require(VALUE feature)
2380+
rb_ractor_require(VALUE feature, bool silent)
23612381
{
2382+
// We're about to block on the main ractor, so if we're holding the global lock we'll deadlock.
2383+
ASSERT_vm_unlocking();
2384+
23622385
struct cross_ractor_require *crr;
23632386
VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
23642387
FL_SET_RAW(crr_obj, RUBY_FL_SHAREABLE);
@@ -2368,6 +2391,7 @@ rb_ractor_require(VALUE feature)
23682391
crr->port = ractor_port_new(GET_RACTOR());
23692392
crr->result = Qundef;
23702393
crr->exception = Qundef;
2394+
crr->silent = silent;
23712395

23722396
rb_execution_context_t *ec = GET_EC();
23732397
rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
@@ -2395,7 +2419,7 @@ rb_ractor_require(VALUE feature)
23952419
static VALUE
23962420
ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
23972421
{
2398-
return rb_ractor_require(feature);
2422+
return rb_ractor_require(feature, false);
23992423
}
24002424

24012425
static VALUE

ractor_core.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ void rb_ractor_terminate_all(void);
134134
bool rb_ractor_main_p_(void);
135135
void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th);
136136
void rb_ractor_terminate_atfork(rb_vm_t *vm, rb_ractor_t *th);
137-
VALUE rb_ractor_require(VALUE feature);
137+
VALUE rb_ractor_require(VALUE feature, bool silent);
138138
VALUE rb_ractor_autoload_load(VALUE space, ID id);
139139

140140
VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name);

0 commit comments

Comments
 (0)