@@ -2063,58 +2063,49 @@ vm_ccs_verify(struct rb_class_cc_entries *ccs, ID mid, VALUE klass)
20632063
20642064const rb_callable_method_entry_t * rb_check_overloaded_cme (const rb_callable_method_entry_t * cme , const struct rb_callinfo * const ci );
20652065
2066- static const struct rb_callcache *
2067- vm_search_cc ( const VALUE klass , const struct rb_callinfo * const ci )
2066+ static void
2067+ vm_evict_cc ( VALUE klass , VALUE cc_tbl , ID mid )
20682068{
2069- const ID mid = vm_ci_mid (ci );
2070- VALUE cc_tbl = RCLASS_WRITABLE_CC_TBL (klass );
2071- struct rb_class_cc_entries * ccs = NULL ;
2072- VALUE ccs_data ;
2069+ ASSERT_vm_locking ();
20732070
2074- if (cc_tbl ) {
2075- // CCS data is keyed on method id, so we don't need the method id
2076- // for doing comparisons in the `for` loop below.
2077- if (rb_managed_id_table_lookup (cc_tbl , mid , & ccs_data )) {
2078- ccs = (struct rb_class_cc_entries * )ccs_data ;
2079- const int ccs_len = ccs -> len ;
2080-
2081- if (UNLIKELY (METHOD_ENTRY_INVALIDATED (ccs -> cme ))) {
2082- rb_managed_id_table_delete (cc_tbl , mid );
2083- rb_vm_ccs_free (ccs );
2084- ccs = NULL ;
2085- }
2086- else {
2087- VM_ASSERT (vm_ccs_verify (ccs , mid , klass ));
2071+ if (rb_multi_ractor_p ()) {
2072+ if (RCLASS_WRITABLE_CC_TBL (klass ) != cc_tbl ) {
2073+ // Another ractor updated the CC table while we were waiting on the VM lock.
2074+ // We have to retry.
2075+ return ;
2076+ }
20882077
2089- // We already know the method id is correct because we had
2090- // to look up the ccs_data by method id. All we need to
2091- // compare is argc and flag
2092- unsigned int argc = vm_ci_argc (ci );
2093- unsigned int flag = vm_ci_flag (ci );
2078+ struct rb_class_cc_entries * ccs = NULL ;
2079+ rb_managed_id_table_lookup (cc_tbl , mid , (VALUE * )& ccs );
20942080
2095- for ( int i = 0 ; i < ccs_len ; i ++ ) {
2096- unsigned int ccs_ci_argc = ccs -> entries [ i ]. argc ;
2097- unsigned int ccs_ci_flag = ccs -> entries [ i ]. flag ;
2098- const struct rb_callcache * ccs_cc = ccs -> entries [ i ]. cc ;
2081+ if (! ccs || ! METHOD_ENTRY_INVALIDATED ( ccs -> cme ) ) {
2082+ // Another ractor replaced that entry while we were waiting on the VM lock.
2083+ return ;
2084+ }
20992085
2100- VM_ASSERT (IMEMO_TYPE_P (ccs_cc , imemo_callcache ));
2086+ VALUE new_table = rb_vm_cc_table_dup (cc_tbl );
2087+ rb_vm_cc_table_delete (new_table , mid );
2088+ RB_OBJ_ATOMIC_WRITE (klass , & RCLASS_WRITABLE_CC_TBL (klass ), new_table );
2089+ }
2090+ else {
2091+ rb_vm_cc_table_delete (cc_tbl , mid );
2092+ }
2093+ }
21012094
2102- if (ccs_ci_argc == argc && ccs_ci_flag == flag ) {
2103- RB_DEBUG_COUNTER_INC (cc_found_in_ccs );
2095+ static const struct rb_callcache *
2096+ vm_populate_cc (VALUE klass , const struct rb_callinfo * const ci , ID mid )
2097+ {
2098+ ASSERT_vm_locking ();
21042099
2105- VM_ASSERT ( vm_cc_cme ( ccs_cc ) -> called_id == mid );
2106- VM_ASSERT ( ccs_cc -> klass == klass ) ;
2107- VM_ASSERT (! METHOD_ENTRY_INVALIDATED ( vm_cc_cme ( ccs_cc ))) ;
2100+ VALUE cc_tbl = RCLASS_WRITABLE_CC_TBL ( klass );
2101+ const VALUE original_cc_table = cc_tbl ;
2102+ struct rb_class_cc_entries * ccs = NULL ;
21082103
2109- return ccs_cc ;
2110- }
2111- }
2112- }
2113- }
2104+ if (!cc_tbl ) {
2105+ cc_tbl = rb_vm_cc_table_create (1 );
21142106 }
2115- else {
2116- cc_tbl = rb_vm_cc_table_create (2 );
2117- RCLASS_WRITE_CC_TBL (klass , cc_tbl );
2107+ else if (rb_multi_ractor_p ()) {
2108+ cc_tbl = rb_vm_cc_table_dup (cc_tbl );
21182109 }
21192110
21202111 RB_DEBUG_COUNTER_INC (cc_not_found_in_ccs );
@@ -2146,11 +2137,7 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
21462137 if (ccs == NULL ) {
21472138 VM_ASSERT (cc_tbl );
21482139
2149- if (LIKELY (rb_managed_id_table_lookup (cc_tbl , mid , & ccs_data ))) {
2150- // rb_callable_method_entry() prepares ccs.
2151- ccs = (struct rb_class_cc_entries * )ccs_data ;
2152- }
2153- else {
2140+ if (!LIKELY (rb_managed_id_table_lookup (cc_tbl , mid , (VALUE * )& ccs ))) {
21542141 // TODO: required?
21552142 ccs = vm_ccs_create (klass , cc_tbl , mid , cme );
21562143 }
@@ -2165,6 +2152,91 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
21652152 VM_ASSERT (cme -> called_id == mid );
21662153 VM_ASSERT (vm_cc_cme (cc )-> called_id == mid );
21672154
2155+ if (original_cc_table != cc_tbl ) {
2156+ RB_OBJ_ATOMIC_WRITE (klass , & RCLASS_WRITABLE_CC_TBL (klass ), cc_tbl );
2157+ }
2158+
2159+ return cc ;
2160+ }
2161+
2162+ static const struct rb_callcache *
2163+ vm_lookup_cc (const VALUE klass , const struct rb_callinfo * const ci , ID mid )
2164+ {
2165+ VALUE cc_tbl ;
2166+ struct rb_class_cc_entries * ccs ;
2167+ retry :
2168+ cc_tbl = RUBY_ATOMIC_VALUE_LOAD (RCLASS_WRITABLE_CC_TBL (klass ));
2169+ ccs = NULL ;
2170+
2171+ if (cc_tbl ) {
2172+ // CCS data is keyed on method id, so we don't need the method id
2173+ // for doing comparisons in the `for` loop below.
2174+
2175+ if (rb_managed_id_table_lookup (cc_tbl , mid , (VALUE * )& ccs )) {
2176+ const int ccs_len = ccs -> len ;
2177+
2178+ if (UNLIKELY (METHOD_ENTRY_INVALIDATED (ccs -> cme ))) {
2179+ RB_VM_LOCKING () {
2180+ vm_evict_cc (klass , cc_tbl , mid );
2181+ }
2182+ goto retry ;
2183+ }
2184+ else {
2185+ VM_ASSERT (vm_ccs_verify (ccs , mid , klass ));
2186+
2187+ // We already know the method id is correct because we had
2188+ // to look up the ccs_data by method id. All we need to
2189+ // compare is argc and flag
2190+ unsigned int argc = vm_ci_argc (ci );
2191+ unsigned int flag = vm_ci_flag (ci );
2192+
2193+ for (int i = 0 ; i < ccs_len ; i ++ ) {
2194+ unsigned int ccs_ci_argc = ccs -> entries [i ].argc ;
2195+ unsigned int ccs_ci_flag = ccs -> entries [i ].flag ;
2196+ const struct rb_callcache * ccs_cc = ccs -> entries [i ].cc ;
2197+
2198+ VM_ASSERT (IMEMO_TYPE_P (ccs_cc , imemo_callcache ));
2199+
2200+ if (ccs_ci_argc == argc && ccs_ci_flag == flag ) {
2201+ RB_DEBUG_COUNTER_INC (cc_found_in_ccs );
2202+
2203+ VM_ASSERT (vm_cc_cme (ccs_cc )-> called_id == mid );
2204+ VM_ASSERT (ccs_cc -> klass == klass );
2205+ VM_ASSERT (!METHOD_ENTRY_INVALIDATED (vm_cc_cme (ccs_cc )));
2206+
2207+ return ccs_cc ;
2208+ }
2209+ }
2210+ }
2211+ }
2212+ }
2213+
2214+ RB_GC_GUARD (cc_tbl );
2215+ return NULL ;
2216+ }
2217+
2218+ static const struct rb_callcache *
2219+ vm_search_cc (const VALUE klass , const struct rb_callinfo * const ci )
2220+ {
2221+ const ID mid = vm_ci_mid (ci );
2222+
2223+ const struct rb_callcache * cc = vm_lookup_cc (klass , ci , mid );
2224+ if (cc ) {
2225+ return cc ;
2226+ }
2227+
2228+ RB_VM_LOCKING () {
2229+ if (rb_multi_ractor_p ()) {
2230+ // The CC may have been populated by another ractor while we were waiting on the lock,
2231+ // so we must lookup a second time.
2232+ cc = vm_lookup_cc (klass , ci , mid );
2233+ }
2234+
2235+ if (!cc ) {
2236+ cc = vm_populate_cc (klass , ci , mid );
2237+ }
2238+ }
2239+
21682240 return cc ;
21692241}
21702242
@@ -2175,16 +2247,14 @@ rb_vm_search_method_slowpath(const struct rb_callinfo *ci, VALUE klass)
21752247
21762248 VM_ASSERT_TYPE2 (klass , T_CLASS , T_ICLASS );
21772249
2178- RB_VM_LOCKING () {
2179- cc = vm_search_cc (klass , ci );
2250+ cc = vm_search_cc (klass , ci );
21802251
2181- VM_ASSERT (cc );
2182- VM_ASSERT (IMEMO_TYPE_P (cc , imemo_callcache ));
2183- VM_ASSERT (cc == vm_cc_empty () || cc -> klass == klass );
2184- VM_ASSERT (cc == vm_cc_empty () || callable_method_entry_p (vm_cc_cme (cc )));
2185- VM_ASSERT (cc == vm_cc_empty () || !METHOD_ENTRY_INVALIDATED (vm_cc_cme (cc )));
2186- VM_ASSERT (cc == vm_cc_empty () || vm_cc_cme (cc )-> called_id == vm_ci_mid (ci ));
2187- }
2252+ VM_ASSERT (cc );
2253+ VM_ASSERT (IMEMO_TYPE_P (cc , imemo_callcache ));
2254+ VM_ASSERT (cc == vm_cc_empty () || cc -> klass == klass );
2255+ VM_ASSERT (cc == vm_cc_empty () || callable_method_entry_p (vm_cc_cme (cc )));
2256+ VM_ASSERT (cc == vm_cc_empty () || !METHOD_ENTRY_INVALIDATED (vm_cc_cme (cc )));
2257+ VM_ASSERT (cc == vm_cc_empty () || vm_cc_cme (cc )-> called_id == vm_ci_mid (ci ));
21882258
21892259 return cc ;
21902260}
0 commit comments