Skip to content

Commit 276f0d9

Browse files
committed
[Bug #21881] Separate the master and root box
This change separates the master box from the root box, as the single master copy of boxes. Before this change, the root box is the source of copies, and also it runs builtin classes' code. Builtin code makes changes by requiring files, thus the source of copies is mutable, be different from the past one. This causes different internal states of boxes, depending on when it is created. After this change, all boxes are created from the master box, and the master box never runs code, so it realizes the immutable source of copies. This also makes is possible to separate RubyGems (and other default gems) from each boxes. All boxes will have their own copies of RubyGems. RubyGems has its internal state, but it'll be separated between boxes after this change.
1 parent 5ed1f93 commit 276f0d9

16 files changed

Lines changed: 388 additions & 141 deletions

File tree

box.c

Lines changed: 132 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "darray.h"
2121
#include "zjit.h"
2222

23+
#include "builtin.h"
24+
2325
#include <stdio.h>
2426

2527
#ifdef HAVE_FCNTL_H
@@ -36,8 +38,12 @@ VALUE rb_cBox = 0;
3638
VALUE rb_cBoxEntry = 0;
3739
VALUE rb_mBoxLoader = 0;
3840

39-
static rb_box_t root_box[1]; /* Initialize in initialize_root_box() */
41+
static rb_box_t master_box[1]; /* Initialize in initialize_master_box() */
42+
static rb_box_t *root_box;
4043
static rb_box_t *main_box;
44+
45+
static rb_box_gem_flags_t box_gem_flags[1];
46+
4147
static char *tmp_dir;
4248
static bool tmp_dir_has_dirsep;
4349

@@ -61,15 +67,33 @@ VALUE rb_resolve_feature_path(VALUE klass, VALUE fname);
6167
static VALUE rb_box_inspect(VALUE obj);
6268
static void cleanup_all_local_extensions(VALUE libmap);
6369

70+
void
71+
rb_box_set_gem_flags(rb_box_gem_flags_t *flags)
72+
{
73+
74+
box_gem_flags->gem = flags->gem;
75+
box_gem_flags->error_highlight = flags->error_highlight;
76+
box_gem_flags->did_you_mean = flags->did_you_mean;
77+
box_gem_flags->syntax_suggest = flags->syntax_suggest;
78+
}
79+
6480
void
6581
rb_box_init_done(void)
6682
{
6783
ruby_box_init_done = true;
6884
}
6985

86+
const rb_box_t *
87+
rb_master_box(void)
88+
{
89+
return master_box;
90+
}
91+
7092
const rb_box_t *
7193
rb_root_box(void)
7294
{
95+
if (!root_box) // The root box isn't initialized yet - The Ruby runtime is in setup.
96+
return master_box;
7397
return root_box;
7498
}
7599

@@ -83,12 +107,14 @@ const rb_box_t *
83107
rb_current_box(void)
84108
{
85109
/*
86-
* If RUBY_BOX is not set, the root box is the only available one.
110+
* If RUBY_BOX is not set, the master box is the only available one.
87111
*
88-
* Until the main_box is not initialized, the root box is
112+
* While the root/main boxes are not initialized, the master box is
89113
* the only valid box.
90114
* This early return is to avoid accessing EC before its setup.
91115
*/
116+
if (!root_box)
117+
return master_box;
92118
if (!main_box)
93119
return root_box;
94120

@@ -98,6 +124,8 @@ rb_current_box(void)
98124
const rb_box_t *
99125
rb_loading_box(void)
100126
{
127+
if (!root_box)
128+
return master_box;
101129
if (!main_box)
102130
return root_box;
103131

@@ -133,7 +161,7 @@ box_main_to_s(VALUE obj)
133161
static void
134162
box_entry_initialize(rb_box_t *box)
135163
{
136-
const rb_box_t *root = rb_root_box();
164+
const rb_box_t *master = rb_master_box();
137165

138166
// These will be updated immediately
139167
box->box_object = 0;
@@ -142,15 +170,15 @@ box_entry_initialize(rb_box_t *box)
142170
box->top_self = rb_obj_alloc(rb_cObject);
143171
rb_define_singleton_method(box->top_self, "to_s", box_main_to_s, 0);
144172
rb_define_alias(rb_singleton_class(box->top_self), "inspect", "to_s");
145-
box->load_path = rb_ary_dup(root->load_path);
146-
box->expanded_load_path = rb_ary_dup(root->expanded_load_path);
173+
box->load_path = rb_ary_dup(master->load_path);
174+
box->expanded_load_path = rb_ary_dup(master->expanded_load_path);
147175
box->load_path_snapshot = rb_ary_new();
148176
box->load_path_check_cache = 0;
149-
box->loaded_features = rb_ary_dup(root->loaded_features);
177+
box->loaded_features = rb_ary_dup(master->loaded_features);
150178
box->loaded_features_snapshot = rb_ary_new();
151179
box->loaded_features_index = st_init_numtable();
152-
box->loaded_features_realpaths = rb_hash_dup(root->loaded_features_realpaths);
153-
box->loaded_features_realpath_map = rb_hash_dup(root->loaded_features_realpath_map);
180+
box->loaded_features_realpaths = rb_hash_dup(master->loaded_features_realpaths);
181+
box->loaded_features_realpath_map = rb_hash_dup(master->loaded_features_realpath_map);
154182
box->loading_table = st_init_strtable();
155183
box->ruby_dln_libmap = rb_hash_new_with_size(0);
156184
box->gvar_tbl = rb_hash_new_with_size(0);
@@ -227,7 +255,7 @@ free_loaded_feature_index_i(st_data_t key, st_data_t value, st_data_t arg)
227255
}
228256

229257
static void
230-
box_root_free(void *ptr)
258+
free_box_st_tables(void *ptr)
231259
{
232260
rb_box_t *box = (rb_box_t *)ptr;
233261
if (box->loading_table) {
@@ -274,7 +302,7 @@ box_entry_free(void *ptr)
274302

275303
cleanup_all_local_extensions(box->ruby_dln_libmap);
276304

277-
box_root_free(ptr);
305+
free_box_st_tables(ptr);
278306
SIZED_FREE(box);
279307
}
280308

@@ -303,11 +331,11 @@ static const rb_data_type_t rb_box_data_type = {
303331
0, 0, RUBY_TYPED_FREE_IMMEDIATELY // TODO: enable RUBY_TYPED_WB_PROTECTED when inserting write barriers
304332
};
305333

306-
static const rb_data_type_t rb_root_box_data_type = {
307-
"Ruby::Box::Root",
334+
static const rb_data_type_t rb_master_box_data_type = {
335+
"Ruby::Box::Master",
308336
{
309337
rb_box_entry_mark,
310-
box_root_free,
338+
free_box_st_tables,
311339
box_entry_memsize,
312340
rb_box_gc_update_references,
313341
},
@@ -340,7 +368,7 @@ rb_get_box_t(VALUE box)
340368
VM_ASSERT(box);
341369

342370
if (NIL_P(box))
343-
return root_box;
371+
return (rb_box_t *)rb_root_box();
344372

345373
VM_ASSERT(BOX_OBJ_P(box));
346374

@@ -394,6 +422,14 @@ box_initialize(VALUE box_value)
394422

395423
rb_ivar_set(box_value, id_box_entry, entry);
396424

425+
if (ruby_box_init_done) {
426+
if (box_gem_flags->gem) {
427+
rb_vm_call_cfunc_in_box(Qnil, rb_define_gem_modules, (VALUE)box_gem_flags, Qnil,
428+
rb_str_new_cstr("before_prelude.user.dummy"), (const rb_box_t *)box);
429+
rb_load_gem_prelude((VALUE)box);
430+
}
431+
}
432+
397433
// Invalidate ZJIT code that assumes only the root box is active
398434
rb_zjit_invalidate_root_box();
399435

@@ -845,49 +881,52 @@ rb_box_require_relative(VALUE box, VALUE fname)
845881
}
846882

847883
static void
848-
initialize_root_box(void)
884+
initialize_master_box(void)
849885
{
850886
rb_vm_t *vm = GET_VM();
851-
rb_box_t *root = (rb_box_t *)rb_root_box();
887+
rb_box_t *master = (rb_box_t *)rb_master_box();
852888

853-
root->load_path = rb_ary_new();
854-
root->expanded_load_path = rb_ary_hidden_new(0);
855-
root->load_path_snapshot = rb_ary_hidden_new(0);
856-
root->load_path_check_cache = 0;
857-
rb_define_singleton_method(root->load_path, "resolve_feature_path", rb_resolve_feature_path, 1);
889+
master->load_path = rb_ary_new();
890+
master->expanded_load_path = rb_ary_hidden_new(0);
891+
master->load_path_snapshot = rb_ary_hidden_new(0);
892+
master->load_path_check_cache = 0;
893+
rb_define_singleton_method(master->load_path, "resolve_feature_path", rb_resolve_feature_path, 1);
858894

859-
root->loaded_features = rb_ary_new();
860-
root->loaded_features_snapshot = rb_ary_hidden_new(0);
861-
root->loaded_features_index = st_init_numtable();
862-
root->loaded_features_realpaths = rb_hash_new();
863-
rb_obj_hide(root->loaded_features_realpaths);
864-
root->loaded_features_realpath_map = rb_hash_new();
865-
rb_obj_hide(root->loaded_features_realpath_map);
895+
master->loaded_features = rb_ary_new();
896+
master->loaded_features_snapshot = rb_ary_hidden_new(0);
897+
master->loaded_features_index = st_init_numtable();
898+
master->loaded_features_realpaths = rb_hash_new();
899+
rb_obj_hide(master->loaded_features_realpaths);
900+
master->loaded_features_realpath_map = rb_hash_new();
901+
rb_obj_hide(master->loaded_features_realpath_map);
866902

867-
root->ruby_dln_libmap = rb_hash_new_with_size(0);
868-
root->gvar_tbl = rb_hash_new_with_size(0);
869-
root->classext_cow_classes = NULL; // classext CoW never happen on the root box
903+
master->ruby_dln_libmap = rb_hash_new_with_size(0);
904+
master->gvar_tbl = rb_hash_new_with_size(0);
905+
master->classext_cow_classes = NULL; // classext CoW never happen on the master box
870906

871-
vm->root_box = root;
907+
vm->master_box = master;
872908

873909
if (rb_box_available()) {
874-
VALUE root_box, entry;
910+
VALUE master_box, entry;
875911
ID id_box_entry;
876912
CONST_ID(id_box_entry, "__box_entry__");
877913

878-
root_box = rb_obj_alloc(rb_cBox);
879-
RCLASS_SET_PRIME_CLASSEXT_WRITABLE(root_box, true);
880-
RCLASS_SET_CONST_TBL(root_box, RCLASSEXT_CONST_TBL(RCLASS_EXT_PRIME(rb_cObject)), true);
914+
master_box = rb_obj_alloc(rb_cBox);
915+
RCLASS_SET_PRIME_CLASSEXT_WRITABLE(master_box, true);
916+
RCLASS_SET_CONST_TBL(master_box, RCLASSEXT_CONST_TBL(RCLASS_EXT_PRIME(rb_cObject)), true);
917+
918+
master->box_id = box_generate_id();
919+
master->box_object = master_box;
881920

882-
root->box_id = box_generate_id();
883-
root->box_object = root_box;
921+
entry = TypedData_Wrap_Struct(rb_cBoxEntry, &rb_master_box_data_type, master);
922+
rb_ivar_set(master_box, id_box_entry, entry);
884923

885-
entry = TypedData_Wrap_Struct(rb_cBoxEntry, &rb_root_box_data_type, root);
886-
rb_ivar_set(root_box, id_box_entry, entry);
924+
rb_gc_register_mark_object(master_box);
925+
rb_gc_register_mark_object(entry);
887926
}
888927
else {
889-
root->box_id = 1;
890-
root->box_object = Qnil;
928+
master->box_id = 1;
929+
master->box_object = Qnil;
891930
}
892931
}
893932

@@ -911,11 +950,26 @@ static int box_experimental_warned = 0;
911950

912951
RUBY_EXTERN const char ruby_api_version_name[];
913952

914-
void
915-
rb_initialize_main_box(void)
953+
static VALUE
954+
box_value_initialize(bool root, bool user, bool optional)
916955
{
917956
rb_box_t *box;
918-
VALUE main_box_value;
957+
VALUE box_value = rb_class_new_instance(0, NULL, rb_cBox);
958+
959+
VM_ASSERT(BOX_OBJ_P(box_value));
960+
961+
box = rb_get_box_t(box_value);
962+
box->box_object = box_value;
963+
box->is_root = root;
964+
box->is_user = user;
965+
box->is_optional = optional;
966+
return box_value;
967+
}
968+
969+
void
970+
rb_initialize_mandatory_boxes(void)
971+
{
972+
VALUE root_box_value, main_box_value;
919973
rb_vm_t *vm = GET_VM();
920974

921975
VM_ASSERT(rb_box_available());
@@ -928,19 +982,18 @@ rb_initialize_main_box(void)
928982
box_experimental_warned = 1;
929983
}
930984

931-
main_box_value = rb_class_new_instance(0, NULL, rb_cBox);
932-
VM_ASSERT(BOX_OBJ_P(main_box_value));
933-
box = rb_get_box_t(main_box_value);
934-
box->box_object = main_box_value;
935-
box->is_user = true;
936-
box->is_optional = false;
985+
root_box_value = box_value_initialize(true, false, false);
986+
main_box_value = box_value_initialize(false, true, false);
937987

988+
rb_const_set(rb_cBox, rb_intern("ROOT"), root_box_value);
938989
rb_const_set(rb_cBox, rb_intern("MAIN"), main_box_value);
939990

940-
vm->main_box = main_box = box;
991+
vm->root_box = root_box = rb_get_box_t(root_box_value);
992+
vm->main_box = main_box = rb_get_box_t(main_box_value);
941993

942994
// create the writable classext of ::Object explicitly to finalize the set of visible top-level constants
943-
RCLASS_EXT_WRITABLE_IN_BOX(rb_cObject, box);
995+
RCLASS_EXT_WRITABLE_IN_BOX(rb_cObject, root_box);
996+
RCLASS_EXT_WRITABLE_IN_BOX(rb_cObject, main_box);
944997
}
945998

946999
static VALUE
@@ -949,12 +1002,15 @@ rb_box_inspect(VALUE obj)
9491002
rb_box_t *box;
9501003
VALUE r;
9511004
if (obj == Qfalse) {
952-
r = rb_str_new_cstr("#<Ruby::Box:root>");
1005+
r = rb_str_new_cstr("#<Ruby::Box:master>");
9531006
return r;
9541007
}
9551008
box = rb_get_box_t(obj);
9561009
r = rb_str_new_cstr("#<Ruby::Box:");
9571010
rb_str_concat(r, rb_funcall(LONG2NUM(box->box_id), rb_intern("to_s"), 0));
1011+
if (BOX_MASTER_P(box)) {
1012+
rb_str_cat_cstr(r, ",master");
1013+
}
9581014
if (BOX_ROOT_P(box)) {
9591015
rb_str_cat_cstr(r, ",root");
9601016
}
@@ -986,9 +1042,9 @@ box_define_loader_method(const char *name)
9861042
}
9871043

9881044
void
989-
Init_root_box(void)
1045+
Init_master_box(void)
9901046
{
991-
root_box->loading_table = st_init_strtable();
1047+
master_box->loading_table = st_init_strtable();
9921048
}
9931049

9941050
void
@@ -1003,6 +1059,13 @@ Init_enable_box(void)
10031059
}
10041060
}
10051061

1062+
/* :nodoc: */
1063+
static VALUE
1064+
rb_box_s_master(VALUE recv)
1065+
{
1066+
return master_box->box_object;
1067+
}
1068+
10061069
/* :nodoc: */
10071070
static VALUE
10081071
rb_box_s_root(VALUE recv)
@@ -1017,6 +1080,14 @@ rb_box_s_main(VALUE recv)
10171080
return main_box->box_object;
10181081
}
10191082

1083+
/* :nodoc: */
1084+
static VALUE
1085+
rb_box_master_p(VALUE box_value)
1086+
{
1087+
const rb_box_t *box = (const rb_box_t *)rb_get_box_t(box_value);
1088+
return RBOOL(BOX_MASTER_P(box));
1089+
}
1090+
10201091
/* :nodoc: */
10211092
static VALUE
10221093
rb_box_root_p(VALUE box_value)
@@ -1151,7 +1222,7 @@ rb_f_dump_classext(VALUE recv, VALUE klass)
11511222
box = RCLASSEXT_BOX(ext);
11521223
snprintf(buf, 2048, "Prime classext box(%ld,%s), readable(%s), writable(%s)\n",
11531224
box->box_id,
1154-
BOX_ROOT_P(box) ? "root" : (BOX_MAIN_P(box) ? "main" : "optional"),
1225+
BOX_MASTER_P(box) ? "master" : (BOX_ROOT_P(box) ? "root" : (BOX_MAIN_P(box) ? "main" : "optional")),
11551226
RCLASS_PRIME_CLASSEXT_READABLE_P(klass) ? "t" : "f",
11561227
RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass) ? "t" : "f");
11571228
rb_str_cat_cstr(res, buf);
@@ -1193,7 +1264,7 @@ Init_Box(void)
11931264
rb_cBoxEntry = rb_define_class_under(rb_cBox, "Entry", rb_cObject);
11941265
rb_define_alloc_func(rb_cBoxEntry, rb_box_entry_alloc);
11951266

1196-
initialize_root_box();
1267+
initialize_master_box();
11971268

11981269
/* :nodoc: */
11991270
rb_mBoxLoader = rb_define_module_under(rb_cBox, "Loader");
@@ -1204,8 +1275,10 @@ Init_Box(void)
12041275
if (rb_box_available()) {
12051276
rb_include_module(rb_cObject, rb_mBoxLoader);
12061277

1278+
rb_define_singleton_method(rb_cBox, "master", rb_box_s_master, 0);
12071279
rb_define_singleton_method(rb_cBox, "root", rb_box_s_root, 0);
12081280
rb_define_singleton_method(rb_cBox, "main", rb_box_s_main, 0);
1281+
rb_define_method(rb_cBox, "master?", rb_box_master_p, 0);
12091282
rb_define_method(rb_cBox, "root?", rb_box_root_p, 0);
12101283
rb_define_method(rb_cBox, "main?", rb_box_main_p, 0);
12111284

0 commit comments

Comments
 (0)