@@ -499,13 +499,26 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo
499
499
500
500
* variation_created = false;
501
501
502
+ // Fast path: if the shape has a single child, we can check it without a lock
503
+ struct rb_id_table * edges = RUBY_ATOMIC_PTR_LOAD (shape -> edges );
504
+ if (edges && SINGLE_CHILD_P (edges )) {
505
+ rb_shape_t * child = SINGLE_CHILD (edges );
506
+ if (child -> edge_name == id ) {
507
+ return child ;
508
+ }
509
+ }
510
+
502
511
RB_VM_LOCK_ENTER ();
503
512
{
513
+ // The situation may have changed while we waited for the lock.
514
+ // So we load the edge again.
515
+ edges = RUBY_ATOMIC_PTR_LOAD (shape -> edges );
516
+
504
517
// If the current shape has children
505
- if (shape -> edges ) {
518
+ if (edges ) {
506
519
// Check if it only has one child
507
- if (SINGLE_CHILD_P (shape -> edges )) {
508
- rb_shape_t * child = SINGLE_CHILD (shape -> edges );
520
+ if (SINGLE_CHILD_P (edges )) {
521
+ rb_shape_t * child = SINGLE_CHILD (edges );
509
522
// If the one child has a matching edge name, then great,
510
523
// we found what we want.
511
524
if (child -> edge_name == id ) {
@@ -515,7 +528,7 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo
515
528
else {
516
529
// If it has more than one child, do a hash lookup to find it.
517
530
VALUE lookup_result ;
518
- if (rb_id_table_lookup (shape -> edges , id , & lookup_result )) {
531
+ if (rb_id_table_lookup (edges , id , & lookup_result )) {
519
532
res = (rb_shape_t * )lookup_result ;
520
533
}
521
534
}
@@ -531,22 +544,26 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo
531
544
else {
532
545
rb_shape_t * new_shape = rb_shape_alloc_new_child (id , shape , shape_type );
533
546
534
- if (!shape -> edges ) {
547
+ if (!edges ) {
535
548
// If the shape had no edge yet, we can directly set the new child
536
- shape -> edges = TAG_SINGLE_CHILD (new_shape );
549
+ edges = TAG_SINGLE_CHILD (new_shape );
537
550
}
538
551
else {
539
552
// If the edge was single child we need to allocate a table.
540
553
if (SINGLE_CHILD_P (shape -> edges )) {
541
- rb_shape_t * old_child = SINGLE_CHILD (shape -> edges );
542
- shape -> edges = rb_id_table_create (2 );
543
- rb_id_table_insert (shape -> edges , old_child -> edge_name , (VALUE )old_child );
554
+ rb_shape_t * old_child = SINGLE_CHILD (edges );
555
+ edges = rb_id_table_create (2 );
556
+ rb_id_table_insert (edges , old_child -> edge_name , (VALUE )old_child );
544
557
}
545
558
546
- rb_id_table_insert (shape -> edges , new_shape -> edge_name , (VALUE )new_shape );
559
+ rb_id_table_insert (edges , new_shape -> edge_name , (VALUE )new_shape );
547
560
* variation_created = true;
548
561
}
549
562
563
+ // We must use an atomic when setting the edges to ensure the writes
564
+ // from rb_shape_alloc_new_child are committed.
565
+ RUBY_ATOMIC_PTR_SET (shape -> edges , edges );
566
+
550
567
res = new_shape ;
551
568
}
552
569
}
0 commit comments