@@ -251,14 +251,36 @@ 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
+ // Lambda that will GC entries from the secondary map that are no longer present
255
+ // in the primary map.
256
+ VALUE gc_secondary_map = Qnil ;
257
+
258
+ extern VALUE weak_obj_cache ;
259
+
254
260
static void SecondaryMap_Init () {
255
261
rb_gc_register_address (& secondary_map );
262
+ rb_gc_register_address (& gc_secondary_map );
256
263
secondary_map = rb_hash_new ();
264
+ gc_secondary_map = rb_eval_string (
265
+ "->(secondary, weak) {\n"
266
+ " secondary.delete_if { |k, v| !weak.key?(v) }\n"
267
+ "}\n" );
268
+ }
269
+
270
+ static void SecondaryMap_MaybeGC () {
271
+ size_t weak_len = NUM2ULL (rb_funcall (weak_obj_cache , rb_intern ("length" ), 0 ));
272
+ size_t secondary_len = RHASH_SIZE (secondary_map );
273
+ size_t waste = secondary_len - weak_len ;
274
+ PBRUBY_ASSERT (secondary_len >= weak_len );
275
+ if (waste > 1000 && waste > secondary_len * 0.2 ) {
276
+ rb_funcall (gc_secondary_map , rb_intern ("call" ), 2 , secondary_map , weak_obj_cache );
277
+ }
257
278
}
258
279
259
280
static VALUE SecondaryMap_Get (VALUE key ) {
260
281
VALUE ret = rb_hash_lookup (secondary_map , key );
261
282
if (ret == Qnil ) {
283
+ SecondaryMap_MaybeGC ();
262
284
ret = rb_eval_string ("Object.new" );
263
285
rb_hash_aset (secondary_map , key , ret );
264
286
}
0 commit comments