@@ -499,13 +499,27 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, b
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 );
522
+
509
523
// If the one child has a matching edge name, then great,
510
524
// we found what we want.
511
525
if (child -> edge_name == id ) {
@@ -515,7 +529,7 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, b
515
529
else {
516
530
// If it has more than one child, do a hash lookup to find it.
517
531
VALUE lookup_result ;
518
- if (rb_id_table_lookup (shape -> edges , id , & lookup_result )) {
532
+ if (rb_id_table_lookup (edges , id , & lookup_result )) {
519
533
res = (rb_shape_t * )lookup_result ;
520
534
}
521
535
}
@@ -531,22 +545,26 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, b
531
545
else {
532
546
rb_shape_t * new_shape = rb_shape_alloc_new_child (id , shape , shape_type );
533
547
534
- if (!shape -> edges ) {
548
+ if (!edges ) {
535
549
// If the shape had no edge yet, we can directly set the new child
536
- shape -> edges = TAG_SINGLE_CHILD (new_shape );
550
+ edges = TAG_SINGLE_CHILD (new_shape );
537
551
}
538
552
else {
539
553
// If the edge was single child we need to allocate a table.
540
554
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 );
555
+ rb_shape_t * old_child = SINGLE_CHILD (edges );
556
+ edges = rb_id_table_create (2 );
557
+ rb_id_table_insert (edges , old_child -> edge_name , (VALUE )old_child );
544
558
}
545
559
546
- rb_id_table_insert (shape -> edges , new_shape -> edge_name , (VALUE )new_shape );
560
+ rb_id_table_insert (edges , new_shape -> edge_name , (VALUE )new_shape );
547
561
* variation_created = true;
548
562
}
549
563
564
+ // We must use an atomic when setting the edges to ensure the writes
565
+ // from rb_shape_alloc_new_child are committed.
566
+ RUBY_ATOMIC_PTR_SET (shape -> edges , edges );
567
+
550
568
res = new_shape ;
551
569
}
552
570
}
0 commit comments