summaryrefslogtreecommitdiff
path: root/ractor.c
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2025-06-13 14:25:42 +0200
committerJean Boussier <[email protected]>2025-06-13 18:27:52 +0200
commite22fc73c66b478a19930788b7d23c6ea48b4bdec (patch)
tree6b12d308e407f53bc17d2d737f9b4e0ae559b2ff /ractor.c
parent0674f7dfb5fa79c5b2158c38f2ae80bc5692922a (diff)
Fix a race condition in object_id for shareable objects
If an object is shareable and has no capacity left, it isn't safe to store the object ID in fields as it requires an object resize which can't be done unless all field reads are synchronized. In this very specific case we create the object_id in advance, before the object is made shareable.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/13609
Diffstat (limited to 'ractor.c')
-rw-r--r--ractor.c23
1 files changed, 20 insertions, 3 deletions
diff --git a/ractor.c b/ractor.c
index 2b9d5b3d5b..bd26c7739d 100644
--- a/ractor.c
+++ b/ractor.c
@@ -1357,9 +1357,26 @@ make_shareable_check_shareable(VALUE obj)
}
}
- if (RB_TYPE_P(obj, T_IMEMO)) {
- return traverse_skip;
- }
+ switch (TYPE(obj)) {
+ case T_IMEMO:
+ return traverse_skip;
+ case T_OBJECT:
+ {
+ // If a T_OBJECT is shared and has no free capacity, we can't safely store the object_id inline,
+ // as it would require to move the object content into an external buffer.
+ // This is only a problem for T_OBJECT, given other types have external fields and can do RCU.
+ // To avoid this issue, we proactively create the object_id.
+ shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
+ attr_index_t capacity = RSHAPE_CAPACITY(shape_id);
+ attr_index_t free_capacity = capacity - RSHAPE_LEN(shape_id);
+ if (!rb_shape_has_object_id(shape_id) && capacity && !free_capacity) {
+ rb_obj_id(obj);
+ }
+ }
+ break;
+ default:
+ break;
+ }
if (!RB_OBJ_FROZEN_RAW(obj)) {
rb_funcall(obj, idFreeze, 0);