@@ -251,6 +251,12 @@ void Arena_register(VALUE module) {
251
251
// The object is used only for its identity; it does not contain any data.
252
252
VALUE secondary_map = Qnil ;
253
253
254
+ // Mutations to the map are under a mutex, because SeconaryMap_MaybeGC()
255
+ // iterates over the map which cannot happen in parallel with insertions, or
256
+ // Ruby will throw:
257
+ // can't add a new key into hash during iteration (RuntimeError)
258
+ VALUE secondary_map_mutex = Qnil ;
259
+
254
260
// Lambda that will GC entries from the secondary map that are no longer present
255
261
// in the primary map.
256
262
VALUE gc_secondary_map = Qnil ;
@@ -260,11 +266,13 @@ extern VALUE weak_obj_cache;
260
266
static void SecondaryMap_Init () {
261
267
rb_gc_register_address (& secondary_map );
262
268
rb_gc_register_address (& gc_secondary_map );
269
+ rb_gc_register_address (& secondary_map_mutex );
263
270
secondary_map = rb_hash_new ();
264
271
gc_secondary_map = rb_eval_string (
265
272
"->(secondary, weak) {\n"
266
273
" secondary.delete_if { |k, v| !weak.key?(v) }\n"
267
274
"}\n" );
275
+ secondary_map_mutex = rb_mutex_new ();
268
276
}
269
277
270
278
static void SecondaryMap_MaybeGC () {
@@ -280,9 +288,11 @@ static void SecondaryMap_MaybeGC() {
280
288
static VALUE SecondaryMap_Get (VALUE key ) {
281
289
VALUE ret = rb_hash_lookup (secondary_map , key );
282
290
if (ret == Qnil ) {
291
+ rb_mutex_lock (secondary_map_mutex );
283
292
SecondaryMap_MaybeGC ();
284
293
ret = rb_eval_string ("Object.new" );
285
294
rb_hash_aset (secondary_map , key , ret );
295
+ rb_mutex_unlock (secondary_map_mutex );
286
296
}
287
297
return ret ;
288
298
}
0 commit comments