Skip to content

Commit e1edc73

Browse files
committed
Ractor: lock around global variable get/set
There's a global id_table `rb_global_tbl` that needs a lock (I used VM lock). In the future, we might use a lock-free rb_id_table if we create such a data structure. Reproduction script that might crash or behave strangely: ```ruby 100.times do Ractor.new do 1_000_000.times do $stderr $stdout $stdin $VERBOSE $stderr $stdout $stdin $VERBOSE $stderr $stdout $stdin $VERBOSE end end end $myglobal0 = nil; $myglobal1 = nil; # ... vim macros to the rescue $myglobal100000 = nil; ```
1 parent dda5a04 commit e1edc73

File tree

2 files changed

+83
-72
lines changed

2 files changed

+83
-72
lines changed

bootstraptest/test_ractor.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,8 @@ def check obj1
562562
r.value[:frozen]
563563
}
564564

565-
# Access to global-variables are prohibited
566-
assert_equal 'can not access global variables $gv from non-main Ractors', %q{
565+
# Access to global-variables are prohibited (read)
566+
assert_equal 'can not access global variable $gv from non-main Ractor', %q{
567567
$gv = 1
568568
r = Ractor.new do
569569
$gv
@@ -576,8 +576,8 @@ def check obj1
576576
end
577577
}
578578

579-
# Access to global-variables are prohibited
580-
assert_equal 'can not access global variables $gv from non-main Ractors', %q{
579+
# Access to global-variables are prohibited (write)
580+
assert_equal 'can not access global variable $gv from non-main Ractor', %q{
581581
r = Ractor.new do
582582
$gv = 1
583583
end

variable.c

Lines changed: 79 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -580,16 +580,18 @@ rb_find_global_entry(ID id)
580580
struct rb_global_entry *entry;
581581
VALUE data;
582582

583-
if (!rb_id_table_lookup(rb_global_tbl, id, &data)) {
584-
entry = NULL;
585-
}
586-
else {
587-
entry = (struct rb_global_entry *)data;
588-
RUBY_ASSERT(entry != NULL);
583+
RB_VM_LOCKING() {
584+
if (!rb_id_table_lookup(rb_global_tbl, id, &data)) {
585+
entry = NULL;
586+
}
587+
else {
588+
entry = (struct rb_global_entry *)data;
589+
RUBY_ASSERT(entry != NULL);
590+
}
589591
}
590592

591593
if (UNLIKELY(!rb_ractor_main_p()) && (!entry || !entry->ractor_local)) {
592-
rb_raise(rb_eRactorIsolationError, "can not access global variables %s from non-main Ractors", rb_id2name(id));
594+
rb_raise(rb_eRactorIsolationError, "can not access global variable %s from non-main Ractor", rb_id2name(id));
593595
}
594596

595597
return entry;
@@ -617,25 +619,28 @@ rb_gvar_undef_compactor(void *var)
617619
static struct rb_global_entry*
618620
rb_global_entry(ID id)
619621
{
620-
struct rb_global_entry *entry = rb_find_global_entry(id);
621-
if (!entry) {
622-
struct rb_global_variable *var;
623-
entry = ALLOC(struct rb_global_entry);
624-
var = ALLOC(struct rb_global_variable);
625-
entry->id = id;
626-
entry->var = var;
627-
entry->ractor_local = false;
628-
var->counter = 1;
629-
var->data = 0;
630-
var->getter = rb_gvar_undef_getter;
631-
var->setter = rb_gvar_undef_setter;
632-
var->marker = rb_gvar_undef_marker;
633-
var->compactor = rb_gvar_undef_compactor;
634-
635-
var->block_trace = 0;
636-
var->trace = 0;
637-
var->namespace_ready = false;
638-
rb_id_table_insert(rb_global_tbl, id, (VALUE)entry);
622+
struct rb_global_entry *entry;
623+
RB_VM_LOCKING() {
624+
entry = rb_find_global_entry(id);
625+
if (!entry) {
626+
struct rb_global_variable *var;
627+
entry = ALLOC(struct rb_global_entry);
628+
var = ALLOC(struct rb_global_variable);
629+
entry->id = id;
630+
entry->var = var;
631+
entry->ractor_local = false;
632+
var->counter = 1;
633+
var->data = 0;
634+
var->getter = rb_gvar_undef_getter;
635+
var->setter = rb_gvar_undef_setter;
636+
var->marker = rb_gvar_undef_marker;
637+
var->compactor = rb_gvar_undef_compactor;
638+
639+
var->block_trace = 0;
640+
var->trace = 0;
641+
var->namespace_ready = false;
642+
rb_id_table_insert(rb_global_tbl, id, (VALUE)entry);
643+
}
639644
}
640645
return entry;
641646
}
@@ -999,15 +1004,17 @@ rb_gvar_set(ID id, VALUE val)
9991004
struct rb_global_entry *entry;
10001005
const rb_namespace_t *ns = rb_current_namespace();
10011006

1002-
entry = rb_global_entry(id);
1007+
RB_VM_LOCKING() {
1008+
entry = rb_global_entry(id);
10031009

1004-
if (USE_NAMESPACE_GVAR_TBL(ns, entry)) {
1005-
rb_hash_aset(ns->gvar_tbl, rb_id2sym(entry->id), val);
1006-
retval = val;
1007-
// TODO: think about trace
1008-
}
1009-
else {
1010-
retval = rb_gvar_set_entry(entry, val);
1010+
if (USE_NAMESPACE_GVAR_TBL(ns, entry)) {
1011+
rb_hash_aset(ns->gvar_tbl, rb_id2sym(entry->id), val);
1012+
retval = val;
1013+
// TODO: think about trace
1014+
}
1015+
else {
1016+
retval = rb_gvar_set_entry(entry, val);
1017+
}
10111018
}
10121019
return retval;
10131020
}
@@ -1022,27 +1029,30 @@ VALUE
10221029
rb_gvar_get(ID id)
10231030
{
10241031
VALUE retval, gvars, key;
1025-
struct rb_global_entry *entry = rb_global_entry(id);
1026-
struct rb_global_variable *var = entry->var;
10271032
const rb_namespace_t *ns = rb_current_namespace();
1028-
1029-
if (USE_NAMESPACE_GVAR_TBL(ns, entry)) {
1030-
gvars = ns->gvar_tbl;
1031-
key = rb_id2sym(entry->id);
1032-
if (RTEST(rb_hash_has_key(gvars, key))) { // this gvar is already cached
1033-
retval = rb_hash_aref(gvars, key);
1033+
// TODO: use lock-free rb_id_table when it's available for use (doesn't yet exist)
1034+
RB_VM_LOCKING() {
1035+
struct rb_global_entry *entry = rb_global_entry(id);
1036+
struct rb_global_variable *var = entry->var;
1037+
1038+
if (USE_NAMESPACE_GVAR_TBL(ns, entry)) {
1039+
gvars = ns->gvar_tbl;
1040+
key = rb_id2sym(entry->id);
1041+
if (RTEST(rb_hash_has_key(gvars, key))) { // this gvar is already cached
1042+
retval = rb_hash_aref(gvars, key);
1043+
}
1044+
else {
1045+
retval = (*var->getter)(entry->id, var->data);
1046+
if (rb_obj_respond_to(retval, rb_intern("clone"), 1)) {
1047+
retval = rb_funcall(retval, rb_intern("clone"), 0);
1048+
}
1049+
rb_hash_aset(gvars, key, retval);
1050+
}
10341051
}
10351052
else {
10361053
retval = (*var->getter)(entry->id, var->data);
1037-
if (rb_obj_respond_to(retval, rb_intern("clone"), 1)) {
1038-
retval = rb_funcall(retval, rb_intern("clone"), 0);
1039-
}
1040-
rb_hash_aset(gvars, key, retval);
10411054
}
10421055
}
1043-
else {
1044-
retval = (*var->getter)(entry->id, var->data);
1045-
}
10461056
return retval;
10471057
}
10481058

@@ -1124,35 +1134,36 @@ rb_f_global_variables(void)
11241134
void
11251135
rb_alias_variable(ID name1, ID name2)
11261136
{
1127-
struct rb_global_entry *entry1, *entry2;
1137+
struct rb_global_entry *entry1 = NULL, *entry2;
11281138
VALUE data1;
11291139
struct rb_id_table *gtbl = rb_global_tbl;
11301140

11311141
if (!rb_ractor_main_p()) {
11321142
rb_raise(rb_eRactorIsolationError, "can not access global variables from non-main Ractors");
11331143
}
11341144

1135-
entry2 = rb_global_entry(name2);
1136-
if (!rb_id_table_lookup(gtbl, name1, &data1)) {
1137-
entry1 = ALLOC(struct rb_global_entry);
1138-
entry1->id = name1;
1139-
rb_id_table_insert(gtbl, name1, (VALUE)entry1);
1140-
}
1141-
else if ((entry1 = (struct rb_global_entry *)data1)->var != entry2->var) {
1142-
struct rb_global_variable *var = entry1->var;
1143-
if (var->block_trace) {
1144-
rb_raise(rb_eRuntimeError, "can't alias in tracer");
1145+
RB_VM_LOCKING() {
1146+
entry2 = rb_global_entry(name2);
1147+
if (!rb_id_table_lookup(gtbl, name1, &data1)) {
1148+
entry1 = ALLOC(struct rb_global_entry);
1149+
entry1->id = name1;
1150+
rb_id_table_insert(gtbl, name1, (VALUE)entry1);
1151+
}
1152+
else if ((entry1 = (struct rb_global_entry *)data1)->var != entry2->var) {
1153+
struct rb_global_variable *var = entry1->var;
1154+
if (var->block_trace) {
1155+
rb_raise(rb_eRuntimeError, "can't alias in tracer");
1156+
}
1157+
var->counter--;
1158+
if (var->counter == 0) {
1159+
free_global_variable(var);
1160+
}
11451161
}
1146-
var->counter--;
1147-
if (var->counter == 0) {
1148-
free_global_variable(var);
1162+
if (entry1) {
1163+
entry2->var->counter++;
1164+
entry1->var = entry2->var;
11491165
}
11501166
}
1151-
else {
1152-
return;
1153-
}
1154-
entry2->var->counter++;
1155-
entry1->var = entry2->var;
11561167
}
11571168

11581169
static void

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy