diff options
| author | Philipp Stephani | 2020-07-23 13:48:43 +0200 |
|---|---|---|
| committer | Philipp Stephani | 2020-07-31 18:05:18 +0200 |
| commit | 8ecca2f09f6bc387412f258c4fc4e3ddf807b2b3 (patch) | |
| tree | 0b0703e5b5e5a0bbc345e5123b395f777ee0508f | |
| parent | e12d1fbc1568cc90b3b99bb6b9f244e5d10e97a4 (diff) | |
| download | emacs-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.c | 82 |
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. */ |
| 162 | static struct emacs_value_storage | 162 | struct 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 | ||
| 356 | static Lisp_Object Vmodule_refs_hash; | 358 | static 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 | |||
| 364 | struct 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 | |||
| 376 | static struct module_global_reference * | ||
| 377 | XMODULE_GLOBAL_REFERENCE (Lisp_Object o) | ||
| 378 | { | ||
| 379 | eassert (PSEUDOVECTORP (o, PVEC_OTHER)); | ||
| 380 | return XUNTAG (o, Lisp_Vectorlike, struct module_global_reference); | ||
| 381 | } | ||
| 382 | |||
| 358 | static emacs_value | 383 | static emacs_value |
| 359 | module_make_global_ref (emacs_env *env, emacs_value ref) | 384 | module_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 | ||
| 383 | static void | 417 | static 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, | |||
| 1404 | void | 1433 | void |
| 1405 | init_module_assertions (bool enable) | 1434 | init_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 |