aboutsummaryrefslogtreecommitdiffstats
path: root/src/alloc.c
diff options
context:
space:
mode:
authorPhilipp Stephani2020-11-27 19:08:55 +0100
committerPhilipp Stephani2020-11-27 20:15:33 +0100
commit23974cfa48b9245658667eff81d132b3aecd2618 (patch)
tree654441136c36d579d366f64f32907bb72e2b3b20 /src/alloc.c
parente7423689208026658fbe8c75523eac6dbe022c39 (diff)
downloademacs-23974cfa48b9245658667eff81d132b3aecd2618.tar.gz
emacs-23974cfa48b9245658667eff81d132b3aecd2618.zip
Fix incorrect handling of module runtime and environment pointers.
We used to store module runtime and environment pointers in the static lists Vmodule_runtimes and Vmodule_environments. However, this is incorrect because these objects have to be kept per-thread. With this naive approach, interleaving module function calls in separate threads leads to environments being removed in the wrong order, which in turn can cause local module values to be incorrectly garbage-collected. The fix isn't completely trivial: specbinding the lists wouldn't work either, because then the garbage collector wouldn't find the environments in other threads than the current ones, again leading to objects being garbage-collected incorrectly. While introducing custom pseudovector types would fix this, it's simpler to put the runtime and environment pointers into the specbinding list as new specbinding kinds. This works since we need to unwind them anyway, and we only ever treat the lists as a stack. The thread switching machinery ensures that the specbinding lists are thread-local, and that all elements of the specbinding lists in all threads are marked during garbage collection. Module assertions now have to walk the specbinding list for the current thread, which is more correct since they now only find environments for the current thread. As a result, we can now remove the faulty Vmodule_runtimes and Vmodule_environments variables entirely. Also add a unit test that exemplifies the problem. It interleaves two module calls in two threads so that the first call ends while the second one is still active. Without this change, this test triggers an assertion failure. * src/lisp.h (enum specbind_tag): Add new tags for module runtimes and environments. * src/eval.c (record_unwind_protect_module): New function to record a module object in the specpdl list. (do_one_unbind): Unwind module objects. (backtrace_eval_unrewind, default_toplevel_binding, lexbound_p) (Fbacktrace__locals): Deal with new specbinding types. (mark_specpdl): Mark module environments as needed. * src/alloc.c (garbage_collect): Remove call to 'mark-modules'. Garbage collection of module values is now handled as part of marking the specpdl of each thread. * src/emacs-module.c (Fmodule_load, funcall_module): Use specpdl to record module runtimes and environments. (module_assert_runtime, module_assert_env, value_to_lisp): Walk through specpdl list instead of list variables. (mark_module_environment): Rename from 'mark_modules'. Don't attempt to walk though current thread's environments only, since that would miss other threads. (initialize_environment, finalize_environment): Don't change Vmodule_environments variable; environments are now in the specpdl list. (finalize_environment_unwind, finalize_runtime_unwind): Make 'extern' since do_one_unbind now calls them. (finalize_runtime_unwind): Don't change Vmodule_runtimes variable; runtimes are now in the specpdl list. (syms_of_module): Remove Vmodule_runtimes and Vmodule_environments. * test/data/emacs-module/mod-test.c (Fmod_test_funcall): New test function. (emacs_module_init): Bind it. * test/src/emacs-module-tests.el (emacs-module-tests--variable): New helper type to guard access to state in a thread-safe way. (emacs-module-tests--wait-for-variable) (emacs-module-tests--change-variable): New helper functions. (emacs-module-tests/interleaved-threads): New unit test.
Diffstat (limited to 'src/alloc.c')
-rw-r--r--src/alloc.c4
1 files changed, 0 insertions, 4 deletions
diff --git a/src/alloc.c b/src/alloc.c
index 2b3643e35bd..504ef179ecc 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6061,10 +6061,6 @@ garbage_collect (void)
6061 mark_fringe_data (); 6061 mark_fringe_data ();
6062#endif 6062#endif
6063 6063
6064#ifdef HAVE_MODULES
6065 mark_modules ();
6066#endif
6067
6068 /* Everything is now marked, except for the data in font caches, 6064 /* Everything is now marked, except for the data in font caches,
6069 undo lists, and finalizers. The first two are compacted by 6065 undo lists, and finalizers. The first two are compacted by
6070 removing an items which aren't reachable otherwise. */ 6066 removing an items which aren't reachable otherwise. */