aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPhilipp Stephani2020-07-23 13:48:43 +0200
committerPhilipp Stephani2020-07-23 14:03:27 +0200
commit5c5eb9790898e4ab10bcbbdb6871947ed3018569 (patch)
treedfdf20215168ca5f78ef66cecc092eccc28167c8 /src
parentfcd43287b3d36a5706760d68b7d88502ebe43a47 (diff)
downloademacs-5c5eb9790898e4ab10bcbbdb6871947ed3018569.tar.gz
emacs-5c5eb9790898e4ab10bcbbdb6871947ed3018569.zip
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'.
Diffstat (limited to 'src')
-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 3d1827c7dad..c47ea9c1950 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -154,11 +154,11 @@ struct emacs_value_frame
154/* A structure that holds an initial frame (so that the first local 154/* A structure that holds an initial frame (so that the first local
155 values require no dynamic allocation) and keeps track of the 155 values require no dynamic allocation) and keeps track of the
156 current frame. */ 156 current frame. */
157static struct emacs_value_storage 157struct emacs_value_storage
158{ 158{
159 struct emacs_value_frame initial; 159 struct emacs_value_frame initial;
160 struct emacs_value_frame *current; 160 struct emacs_value_frame *current;
161} global_storage; 161};
162 162
163 163
164/* Private runtime and environment members. */ 164/* Private runtime and environment members. */
@@ -371,10 +371,35 @@ module_get_environment (struct emacs_runtime *runtime)
371} 371}
372 372
373/* To make global refs (GC-protected global values) keep a hash that 373/* To make global refs (GC-protected global values) keep a hash that
374 maps global Lisp objects to reference counts. */ 374 maps global Lisp objects to 'struct module_global_reference'
375 objects. We store the 'emacs_value' in the hash table so that it
376 is automatically garbage-collected (Bug#42482). */
375 377
376static Lisp_Object Vmodule_refs_hash; 378static Lisp_Object Vmodule_refs_hash;
377 379
380/* Pseudovector type for global references. The pseudovector tag is
381 PVEC_OTHER since these values are never printed and don't need to
382 be special-cased for garbage collection. */
383
384struct module_global_reference {
385 /* Pseudovector header, must come first. */
386 union vectorlike_header header;
387
388 /* Holds the emacs_value for the object. The Lisp_Object stored
389 therein must be the same as the hash key. */
390 struct emacs_value_tag value;
391
392 /* Reference count, always positive. */
393 ptrdiff_t refcount;
394};
395
396static struct module_global_reference *
397XMODULE_GLOBAL_REFERENCE (Lisp_Object o)
398{
399 eassert (PSEUDOVECTORP (o, PVEC_OTHER));
400 return XUNTAG (o, Lisp_Vectorlike, struct module_global_reference);
401}
402
378static emacs_value 403static emacs_value
379module_make_global_ref (emacs_env *env, emacs_value value) 404module_make_global_ref (emacs_env *env, emacs_value value)
380{ 405{
@@ -383,21 +408,30 @@ module_make_global_ref (emacs_env *env, emacs_value value)
383 Lisp_Object new_obj = value_to_lisp (value), hashcode; 408 Lisp_Object new_obj = value_to_lisp (value), hashcode;
384 ptrdiff_t i = hash_lookup (h, new_obj, &hashcode); 409 ptrdiff_t i = hash_lookup (h, new_obj, &hashcode);
385 410
411 /* Note: This approach requires the garbage collector to never move
412 objects. */
413
386 if (i >= 0) 414 if (i >= 0)
387 { 415 {
388 Lisp_Object value = HASH_VALUE (h, i); 416 Lisp_Object value = HASH_VALUE (h, i);
389 EMACS_INT refcount = XFIXNAT (value) + 1; 417 struct module_global_reference *ref = XMODULE_GLOBAL_REFERENCE (value);
390 if (MOST_POSITIVE_FIXNUM < refcount) 418 bool overflow = INT_ADD_WRAPV (ref->refcount, 1, &ref->refcount);
419 if (overflow)
391 overflow_error (); 420 overflow_error ();
392 value = make_fixed_natnum (refcount); 421 return &ref->value;
393 set_hash_value_slot (h, i, value);
394 } 422 }
395 else 423 else
396 { 424 {
397 hash_put (h, new_obj, make_fixed_natnum (1), hashcode); 425 struct module_global_reference *ref
426 = ALLOCATE_PLAIN_PSEUDOVECTOR (struct module_global_reference,
427 PVEC_OTHER);
428 ref->value.v = new_obj;
429 ref->refcount = 1;
430 Lisp_Object value;
431 XSETPSEUDOVECTOR (value, ref, PVEC_OTHER);
432 hash_put (h, new_obj, value, hashcode);
433 return &ref->value;
398 } 434 }
399
400 return allocate_emacs_value (env, &global_storage, new_obj);
401} 435}
402 436
403static void 437static void
@@ -413,23 +447,16 @@ module_free_global_ref (emacs_env *env, emacs_value global_value)
413 447
414 if (i >= 0) 448 if (i >= 0)
415 { 449 {
416 EMACS_INT refcount = XFIXNAT (HASH_VALUE (h, i)) - 1; 450 Lisp_Object value = HASH_VALUE (h, i);
417 if (refcount > 0) 451 struct module_global_reference *ref = XMODULE_GLOBAL_REFERENCE (value);
418 set_hash_value_slot (h, i, make_fixed_natnum (refcount)); 452 eassert (0 < ref->refcount);
419 else 453 if (--ref->refcount == 0)
420 { 454 hash_remove_from_table (h, obj);
421 eassert (refcount == 0);
422 hash_remove_from_table (h, obj);
423 }
424 } 455 }
425 456 else if (module_assertions)
426 if (module_assertions)
427 { 457 {
428 ptrdiff_t count = 0;
429 if (value_storage_contains_p (&global_storage, global_value, &count))
430 return;
431 module_abort ("Global value was not found in list of %"pD"d globals", 458 module_abort ("Global value was not found in list of %"pD"d globals",
432 count); 459 h->count);
433 } 460 }
434} 461}
435 462
@@ -1250,8 +1277,10 @@ value_to_lisp (emacs_value v)
1250 ++num_environments; 1277 ++num_environments;
1251 } 1278 }
1252 /* Also check global values. */ 1279 /* Also check global values. */
1253 if (value_storage_contains_p (&global_storage, v, &num_values)) 1280 struct Lisp_Hash_Table *h = XHASH_TABLE (Vmodule_refs_hash);
1281 if (hash_lookup (h, v->v, NULL) != -1)
1254 goto ok; 1282 goto ok;
1283 INT_ADD_WRAPV (num_values, h->count, &num_values);
1255 module_abort (("Emacs value not found in %"pD"d values " 1284 module_abort (("Emacs value not found in %"pD"d values "
1256 "of %"pD"d environments"), 1285 "of %"pD"d environments"),
1257 num_values, num_environments); 1286 num_values, num_environments);
@@ -1467,10 +1496,7 @@ module_handle_nonlocal_exit (emacs_env *env, enum nonlocal_exit type,
1467void 1496void
1468init_module_assertions (bool enable) 1497init_module_assertions (bool enable)
1469{ 1498{
1470 /* If enabling module assertions, use a hidden environment for
1471 storing the globals. This environment is never freed. */
1472 module_assertions = enable; 1499 module_assertions = enable;
1473 initialize_storage (&global_storage);
1474} 1500}
1475 1501
1476/* Return whether STORAGE contains VALUE. Used to check module 1502/* Return whether STORAGE contains VALUE. Used to check module