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