aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Stephani2020-07-23 13:48:43 +0200
committerPhilipp Stephani2020-07-31 18:05:18 +0200
commit8ecca2f09f6bc387412f258c4fc4e3ddf807b2b3 (patch)
tree0b0703e5b5e5a0bbc345e5123b395f777ee0508f
parente12d1fbc1568cc90b3b99bb6b9f244e5d10e97a4 (diff)
downloademacs-8ecca2f09f6bc387412f258c4fc4e3ddf807b2b3.tar.gz
emacs-8ecca2f09f6bc387412f258c4fc4e3ddf807b2b3.zip
Backport: Fix memory leak for global module objects (Bug#42482).
Instead of storing the global values in a global 'emacs_value_storage' object, store them as hash values alongside the reference counts. That way the garbage collector takes care of cleaning them up. * src/emacs-module.c (global_storage): Remove. (struct module_global_reference): New pseudovector type. (XMODULE_GLOBAL_REFERENCE): New helper function. (module_make_global_ref, module_free_global_ref): Use 'module_global_reference' struct for global reference values. (value_to_lisp, module_handle_nonlocal_exit): Adapt to deletion of 'global_storage'. (cherry picked from commit 5c5eb9790898e4ab10bcbbdb6871947ed3018569)
-rw-r--r--src/emacs-module.c82
1 files changed, 54 insertions, 28 deletions
diff --git a/src/emacs-module.c b/src/emacs-module.c
index 911b82b8a1a..4269b0ba2ac 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -159,11 +159,11 @@ struct emacs_value_frame
159/* A structure that holds an initial frame (so that the first local 159/* A structure that holds an initial frame (so that the first local
160 values require no dynamic allocation) and keeps track of the 160 values require no dynamic allocation) and keeps track of the
161 current frame. */ 161 current frame. */
162static struct emacs_value_storage 162struct emacs_value_storage
163{ 163{
164 struct emacs_value_frame initial; 164 struct emacs_value_frame initial;
165 struct emacs_value_frame *current; 165 struct emacs_value_frame *current;
166} global_storage; 166};
167 167
168 168
169/* Private runtime and environment members. */ 169/* Private runtime and environment members. */
@@ -351,10 +351,35 @@ module_get_environment (struct emacs_runtime *ert)
351} 351}
352 352
353/* To make global refs (GC-protected global values) keep a hash that 353/* To make global refs (GC-protected global values) keep a hash that
354 maps global Lisp objects to reference counts. */ 354 maps global Lisp objects to 'struct module_global_reference'
355 objects. We store the 'emacs_value' in the hash table so that it
356 is automatically garbage-collected (Bug#42482). */
355 357
356static Lisp_Object Vmodule_refs_hash; 358static Lisp_Object Vmodule_refs_hash;
357 359
360/* Pseudovector type for global references. The pseudovector tag is
361 PVEC_OTHER since these values are never printed and don't need to
362 be special-cased for garbage collection. */
363
364struct module_global_reference {
365 /* Pseudovector header, must come first. */
366 union vectorlike_header header;
367
368 /* Holds the emacs_value for the object. The Lisp_Object stored
369 therein must be the same as the hash key. */
370 struct emacs_value_tag value;
371
372 /* Reference count, always positive. */
373 ptrdiff_t refcount;
374};
375
376static struct module_global_reference *
377XMODULE_GLOBAL_REFERENCE (Lisp_Object o)
378{
379 eassert (PSEUDOVECTORP (o, PVEC_OTHER));
380 return XUNTAG (o, Lisp_Vectorlike, struct module_global_reference);
381}
382
358static emacs_value 383static emacs_value
359module_make_global_ref (emacs_env *env, emacs_value ref) 384module_make_global_ref (emacs_env *env, emacs_value ref)
360{ 385{
@@ -363,21 +388,30 @@ module_make_global_ref (emacs_env *env, emacs_value ref)
363 Lisp_Object new_obj = value_to_lisp (ref), hashcode; 388 Lisp_Object new_obj = value_to_lisp (ref), hashcode;
364 ptrdiff_t i = hash_lookup (h, new_obj, &hashcode); 389 ptrdiff_t i = hash_lookup (h, new_obj, &hashcode);
365 390
391 /* Note: This approach requires the garbage collector to never move
392 objects. */
393
366 if (i >= 0) 394 if (i >= 0)
367 { 395 {
368 Lisp_Object value = HASH_VALUE (h, i); 396 Lisp_Object value = HASH_VALUE (h, i);
369 EMACS_INT refcount = XFIXNAT (value) + 1; 397 struct module_global_reference *ref = XMODULE_GLOBAL_REFERENCE (value);
370 if (MOST_POSITIVE_FIXNUM < refcount) 398 bool overflow = INT_ADD_WRAPV (ref->refcount, 1, &ref->refcount);
399 if (overflow)
371 overflow_error (); 400 overflow_error ();
372 value = make_fixed_natnum (refcount); 401 return &ref->value;
373 set_hash_value_slot (h, i, value);
374 } 402 }
375 else 403 else
376 { 404 {
377 hash_put (h, new_obj, make_fixed_natnum (1), hashcode); 405 struct module_global_reference *ref
406 = ALLOCATE_PLAIN_PSEUDOVECTOR (struct module_global_reference,
407 PVEC_OTHER);
408 ref->value.v = new_obj;
409 ref->refcount = 1;
410 Lisp_Object value;
411 XSETPSEUDOVECTOR (value, ref, PVEC_OTHER);
412 hash_put (h, new_obj, value, hashcode);
413 return &ref->value;
378 } 414 }
379
380 return allocate_emacs_value (env, &global_storage, new_obj);
381} 415}
382 416
383static void 417static void
@@ -393,23 +427,16 @@ module_free_global_ref (emacs_env *env, emacs_value ref)
393 427
394 if (i >= 0) 428 if (i >= 0)
395 { 429 {
396 EMACS_INT refcount = XFIXNAT (HASH_VALUE (h, i)) - 1; 430 Lisp_Object value = HASH_VALUE (h, i);
397 if (refcount > 0) 431 struct module_global_reference *ref = XMODULE_GLOBAL_REFERENCE (value);
398 set_hash_value_slot (h, i, make_fixed_natnum (refcount)); 432 eassert (0 < ref->refcount);
399 else 433 if (--ref->refcount == 0)
400 { 434 hash_remove_from_table (h, obj);
401 eassert (refcount == 0);
402 hash_remove_from_table (h, obj);
403 }
404 } 435 }
405 436 else if (module_assertions)
406 if (module_assertions)
407 { 437 {
408 ptrdiff_t count = 0;
409 if (value_storage_contains_p (&global_storage, ref, &count))
410 return;
411 module_abort ("Global value was not found in list of %"pD"d globals", 438 module_abort ("Global value was not found in list of %"pD"d globals",
412 count); 439 h->count);
413 } 440 }
414} 441}
415 442
@@ -1190,8 +1217,10 @@ value_to_lisp (emacs_value v)
1190 ++num_environments; 1217 ++num_environments;
1191 } 1218 }
1192 /* Also check global values. */ 1219 /* Also check global values. */
1193 if (value_storage_contains_p (&global_storage, v, &num_values)) 1220 struct Lisp_Hash_Table *h = XHASH_TABLE (Vmodule_refs_hash);
1221 if (hash_lookup (h, v->v, NULL) != -1)
1194 goto ok; 1222 goto ok;
1223 INT_ADD_WRAPV (num_values, h->count, &num_values);
1195 module_abort (("Emacs value not found in %"pD"d values " 1224 module_abort (("Emacs value not found in %"pD"d values "
1196 "of %"pD"d environments"), 1225 "of %"pD"d environments"),
1197 num_values, num_environments); 1226 num_values, num_environments);
@@ -1404,10 +1433,7 @@ module_handle_nonlocal_exit (emacs_env *env, enum nonlocal_exit type,
1404void 1433void
1405init_module_assertions (bool enable) 1434init_module_assertions (bool enable)
1406{ 1435{
1407 /* If enabling module assertions, use a hidden environment for
1408 storing the globals. This environment is never freed. */
1409 module_assertions = enable; 1436 module_assertions = enable;
1410 initialize_storage (&global_storage);
1411} 1437}
1412 1438
1413/* Return whether STORAGE contains VALUE. Used to check module 1439/* Return whether STORAGE contains VALUE. Used to check module