diff options
| author | Philipp Stephani | 2020-07-23 13:48:43 +0200 |
|---|---|---|
| committer | Philipp Stephani | 2020-07-23 14:03:27 +0200 |
| commit | 5c5eb9790898e4ab10bcbbdb6871947ed3018569 (patch) | |
| tree | dfdf20215168ca5f78ef66cecc092eccc28167c8 /src | |
| parent | fcd43287b3d36a5706760d68b7d88502ebe43a47 (diff) | |
| download | emacs-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.c | 82 |
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. */ |
| 157 | static struct emacs_value_storage | 157 | struct 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 | ||
| 376 | static Lisp_Object Vmodule_refs_hash; | 378 | static 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 | |||
| 384 | struct 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 | |||
| 396 | static struct module_global_reference * | ||
| 397 | XMODULE_GLOBAL_REFERENCE (Lisp_Object o) | ||
| 398 | { | ||
| 399 | eassert (PSEUDOVECTORP (o, PVEC_OTHER)); | ||
| 400 | return XUNTAG (o, Lisp_Vectorlike, struct module_global_reference); | ||
| 401 | } | ||
| 402 | |||
| 378 | static emacs_value | 403 | static emacs_value |
| 379 | module_make_global_ref (emacs_env *env, emacs_value value) | 404 | module_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 | ||
| 403 | static void | 437 | static 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, | |||
| 1467 | void | 1496 | void |
| 1468 | init_module_assertions (bool enable) | 1497 | init_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 |