aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrea Corallo2020-09-06 18:17:00 +0200
committerAndrea Corallo2020-09-06 18:17:00 +0200
commita71f54eff80cb7d7b36326849eea878073963594 (patch)
tree4ec2ca769cd2fe752e8a64a8555dbde0d9aaaeee /src
parenteb8742598874d9bd4c84ff54730527c52d29d7ff (diff)
downloademacs-a71f54eff80cb7d7b36326849eea878073963594.tar.gz
emacs-a71f54eff80cb7d7b36326849eea878073963594.zip
Rework eln deletion strategy for new eln-cache folder structure
When recompiling remove the corresponding stale elns found in the `comp-eln-load-path'. When removing a package remove the corresponding elns too. On Windows both of these are performed only when possible, when it's not the file is renamed as .eln.old and a last attempt to remove this is performed closing the Emacs session. When a file being deleted was loaded by multiple Emacs sessions the last one being closed should delete it. * lisp/emacs-lisp/comp.el (comp-clean-up-stale-eln): New function. (comp-delete-or-replace-file): Rename from `comp--replace-output-file' and update so it can be used for replacing or deleting shared libs safetly. * lisp/emacs-lisp/package.el (package--delete-directory): When native compiled just call `comp-clean-up-stale-eln' for each eln file we want to clean-up. * src/alloc.c (cleanup_vector): Call directly the dynlib_close. * src/comp.c (syms_of_comp): Update for comp_u->cfile removal. Make 'all_loaded_comp_units_h' key-value weak as now the key will be the filename. (load_comp_unit): Register the compilation unit only when the load is fully completed. (register_native_comp_unit): Make the key of all_loaded_comp_units_h the load filename. (eln_load_path_final_clean_up): New function. (dispose_comp_unit) (finish_delayed_disposal_of_comp_units) (dispose_all_remaining_comp_units) (clean_package_user_dir_of_old_comp_units): Remove. (Fcomp__compile_ctxt_to_file): Update for `comp--replace-output-file' -> `comp-delete-or-replace-file' rename. * src/comp.h (dispose_comp_unit) (finish_delayed_disposal_of_comp_units) (dispose_all_remaining_comp_units) (clean_package_user_dir_of_old_comp_units): Remove. (eln_load_path_final_clean_up): Add. (struct Lisp_Native_Comp_Unit): Remove cfile field. * src/emacs.c (Fkill_emacs): Call 'eln_load_path_final_clean_up'. * src/pdumper.c (dump_do_dump_relocation): Do not set comp_u->cfile.
Diffstat (limited to 'src')
-rw-r--r--src/alloc.c3
-rw-r--r--src/comp.c236
-rw-r--r--src/comp.h34
-rw-r--r--src/emacs.c6
-rw-r--r--src/pdumper.c3
5 files changed, 31 insertions, 251 deletions
diff --git a/src/alloc.c b/src/alloc.c
index 6701bf002b7..bde0a16ac15 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3153,7 +3153,8 @@ cleanup_vector (struct Lisp_Vector *vector)
3153 { 3153 {
3154 struct Lisp_Native_Comp_Unit *cu = 3154 struct Lisp_Native_Comp_Unit *cu =
3155 PSEUDOVEC_STRUCT (vector, Lisp_Native_Comp_Unit); 3155 PSEUDOVEC_STRUCT (vector, Lisp_Native_Comp_Unit);
3156 dispose_comp_unit (cu, true); 3156 eassert (cu->handle);
3157 dynlib_close (cu->handle);
3157 } 3158 }
3158 else if (NATIVE_COMP_FLAG 3159 else if (NATIVE_COMP_FLAG
3159 && PSEUDOVECTOR_TYPEP (&vector->header, PVEC_SUBR)) 3160 && PSEUDOVECTOR_TYPEP (&vector->header, PVEC_SUBR))
diff --git a/src/comp.c b/src/comp.c
index 3a56f5f22c6..68a0ead69ae 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -4361,7 +4361,8 @@ DEFUN ("comp--compile-ctxt-to-file", Fcomp__compile_ctxt_to_file,
4361 GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY, 4361 GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY,
4362 SSDATA (tmp_file)); 4362 SSDATA (tmp_file));
4363 4363
4364 CALL2I (comp--replace-output-file, file_name, tmp_file); 4364 CALL1I (comp-clean-up-stale-eln, file_name);
4365 CALL2I (comp-delete-or-replace-file, file_name, tmp_file);
4365 4366
4366 if (!noninteractive) 4367 if (!noninteractive)
4367 unbind_to (count, Qnil); 4368 unbind_to (count, Qnil);
@@ -4438,220 +4439,44 @@ helper_PSEUDOVECTOR_TYPEP_XUNTAG (Lisp_Object a, enum pvec_type code)
4438} 4439}
4439 4440
4440 4441
4441/*********************************/ 4442/* `comp-eln-load-path' clean-up support code. */
4442/* Disposal of compilation units */
4443/*********************************/
4444
4445/*
4446 The problem: Windows does not let us delete an .eln file that has
4447 been loaded by a process. This has two implications in Emacs:
4448
4449 1) It is not possible to recompile a lisp file if the corresponding
4450 .eln file has been loaded. This is because we'd like to use the same
4451 filename, but we can't delete the old .eln file.
4452
4453 2) It is not possible to delete a package using `package-delete'
4454 if an .eln file has been loaded.
4455
4456 * General idea
4457
4458 The solution to these two problems is to move the foo.eln file
4459 somewhere else and have the last Emacs instance using it delete it.
4460 To make it easy to find what files need to be removed we use two approaches.
4461
4462 In the 1) case we rename foo.eln to fooXXXXXX.eln.old in the same
4463 folder. When Emacs is unloading "foo" (either GC'd the native
4464 compilation unit or Emacs is closing (see below)) we delete all the
4465 .eln.old files in the folder where the original foo.eln was stored.
4466
4467 Ideally we'd figure out the new name of foo.eln and delete it if it
4468 ends in .eln.old. There is no simple API to do this in Windows.
4469 GetModuleFileName () returns the original filename, not the current
4470 one. This forces us to put .eln.old files in an agreed upon path.
4471 We cannot use %TEMP% because it may be in another drive and then the
4472 rename operation would fail.
4473
4474 In the 2) case we can't use the same folder where the .eln file
4475 resided, as we are trying to completely remove the package. Since we
4476 are removing packages we can safely move the .eln.old file to
4477 `package-user-dir' as we are sure that that would not mean changing
4478 drives.
4479
4480 * Implementation details
4481
4482 The concept of disposal of a native compilation unit refers to
4483 unloading the shared library and deleting all the .eln.old files in
4484 the directory. These are two separate steps. We'll call them
4485 early-disposal and late-disposal.
4486
4487 There are two data structures used:
4488
4489 - The `all_loaded_comp_units_h` hashtable.
4490
4491 This hashtable is used like an array of weak references to native
4492 compilation units. This hash table is filled by load_comp_unit ()
4493 and dispose_all_remaining_comp_units () iterates over all values
4494 that were not disposed by the GC and performs all disposal steps
4495 when Emacs is closing.
4496
4497 - The `delayed_comp_unit_disposal_list` list.
4498
4499 This is were the dispose_comp_unit () function, when called by the
4500 GC sweep stage, stores the original filenames of the disposed native
4501 compilation units. This is an ad-hoc C structure instead of a Lisp
4502 cons because we need to allocate instances of this structure during
4503 the GC.
4504
4505 The finish_delayed_disposal_of_comp_units () function will iterate
4506 over this list and perform the late-disposal step when Emacs is
4507 closing.
4508
4509*/
4510
4511#ifdef WINDOWSNT
4512#define OLD_ELN_SUFFIX_REGEXP build_string ("\\.eln\\.old\\'")
4513 4443
4514static Lisp_Object all_loaded_comp_units_h; 4444static Lisp_Object all_loaded_comp_units_h;
4515 4445
4516/* We need to allocate instances of this struct during a GC sweep. 4446/* Windows does not let us delete a .eln file that is currently loaded
4517 This is why it can't be transformed into a simple cons. */ 4447 by a process. The strategy is to rename .eln files into .old.eln
4518struct delayed_comp_unit_disposal 4448 instead of removing them when this is not possible and clean-up
4519{ 4449 `comp-eln-load-path' when exiting.
4520 struct delayed_comp_unit_disposal *next;
4521 char *filename;
4522};
4523
4524struct delayed_comp_unit_disposal *delayed_comp_unit_disposal_list;
4525
4526static Lisp_Object
4527return_nil (Lisp_Object arg)
4528{
4529 return Qnil;
4530}
4531
4532/* Tries to remove all *.eln.old files in DIRNAME.
4533 4450
4534 Any error is ignored because it may be due to the file being loaded 4451 Any error is ignored because it may be due to the file being loaded
4535 in another Emacs instance. */ 4452 in another Emacs instance. */
4536static void
4537clean_comp_unit_directory (Lisp_Object dirpath)
4538{
4539 if (NILP (dirpath))
4540 return;
4541 Lisp_Object files_in_dir;
4542 files_in_dir = internal_condition_case_4 (Fdirectory_files, dirpath, Qt,
4543 OLD_ELN_SUFFIX_REGEXP, Qnil, Qt,
4544 return_nil);
4545 FOR_EACH_TAIL (files_in_dir) { DeleteFile (SSDATA (XCAR (files_in_dir))); }
4546}
4547
4548/* Tries to remove all *.eln.old files in `package-user-dir'.
4549
4550 This is called when Emacs is closing to clean any *.eln left from a
4551 deleted package. */
4552void 4453void
4553clean_package_user_dir_of_old_comp_units (void) 4454eln_load_path_final_clean_up (void)
4554{ 4455{
4555 Lisp_Object package_user_dir 4456#ifdef WINDOWSNT
4556 = find_symbol_value (intern ("package-user-dir")); 4457 Lisp_Object return_nil (Lisp_Object arg) { return Qnil; }
4557 if (EQ (package_user_dir, Qunbound) || !STRINGP (package_user_dir))
4558 return;
4559
4560 clean_comp_unit_directory (package_user_dir);
4561}
4562
4563/* This function disposes all compilation units that are still loaded.
4564
4565 It is important that this function is called only right before
4566 Emacs is closed, otherwise we risk running a subr that is
4567 implemented in an unloaded dynamic library. */
4568void
4569dispose_all_remaining_comp_units (void)
4570{
4571 struct Lisp_Hash_Table *h = XHASH_TABLE (all_loaded_comp_units_h);
4572
4573 for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (h); ++i)
4574 {
4575 Lisp_Object k = HASH_KEY (h, i);
4576 if (!EQ (k, Qunbound))
4577 {
4578 Lisp_Object val = HASH_VALUE (h, i);
4579 struct Lisp_Native_Comp_Unit *cu = XNATIVE_COMP_UNIT (val);
4580 dispose_comp_unit (cu, false);
4581 }
4582 }
4583}
4584
4585/* This function finishes the disposal of compilation units that were
4586 passed to `dispose_comp_unit` with DELAY == true.
4587 4458
4588 This function is called when Emacs is idle and when it is about to 4459 Lisp_Object dir_tail = Vcomp_eln_load_path;
4589 close. */ 4460 FOR_EACH_TAIL (dir_tail)
4590void
4591finish_delayed_disposal_of_comp_units (void)
4592{
4593 for (struct delayed_comp_unit_disposal *item
4594 = delayed_comp_unit_disposal_list;
4595 delayed_comp_unit_disposal_list; item = delayed_comp_unit_disposal_list)
4596 { 4461 {
4597 delayed_comp_unit_disposal_list = item->next; 4462 Lisp_Object files_in_dir =
4598 Lisp_Object dirname = internal_condition_case_1 ( 4463 internal_condition_case_4 (Fdirectory_files,
4599 Ffile_name_directory, build_string (item->filename), Qt, return_nil); 4464 concat2 (XCAR (dir_tail),
4600 clean_comp_unit_directory (dirname); 4465 Vcomp_native_version_dir),
4601 xfree (item->filename); 4466 Qt, build_string ("\\.eln\\.old\\'"), Qnil,
4602 xfree (item); 4467 Qt, return_nil);
4468 FOR_EACH_TAIL (files_in_dir)
4469 Fdelete_file (XCAR (files_in_dir), Qnil);
4603 } 4470 }
4604}
4605#endif 4471#endif
4472}
4606 4473
4607/* This function puts the compilation unit in the 4474/* This function puts the compilation unit in the
4608 `all_loaded_comp_units_h` hashmap. */ 4475 `all_loaded_comp_units_h` hashmap. */
4609static void 4476static void
4610register_native_comp_unit (Lisp_Object comp_u) 4477register_native_comp_unit (Lisp_Object comp_u)
4611{ 4478{
4612#ifdef WINDOWSNT 4479 Fputhash (XNATIVE_COMP_UNIT (comp_u)->file, comp_u, all_loaded_comp_units_h);
4613 /* We have to do this since we can't use `gensym'. This function is
4614 called early when loading a dump file and subr.el may not have
4615 been loaded yet. */
4616 static intmax_t count;
4617
4618 Fputhash (make_int (count++), comp_u, all_loaded_comp_units_h);
4619#endif
4620}
4621
4622/* This function disposes compilation units. It is called during the GC sweep
4623 stage and when Emacs is closing.
4624
4625 On Windows the the DELAY parameter specifies whether the native
4626 compilation file will be deleted right away (if necessary) or put
4627 on a list. That list will be dealt with by
4628 `finish_delayed_disposal_of_comp_units`. */
4629void
4630dispose_comp_unit (struct Lisp_Native_Comp_Unit *comp_handle, bool delay)
4631{
4632 eassert (comp_handle->handle);
4633 dynlib_close (comp_handle->handle);
4634#ifdef WINDOWSNT
4635 if (!delay)
4636 {
4637 Lisp_Object dirname = internal_condition_case_1 (
4638 Ffile_name_directory, build_string (comp_handle->cfile), Qt,
4639 return_nil);
4640 if (!NILP (dirname))
4641 clean_comp_unit_directory (dirname);
4642 xfree (comp_handle->cfile);
4643 comp_handle->cfile = NULL;
4644 }
4645 else
4646 {
4647 struct delayed_comp_unit_disposal *head;
4648 head = xmalloc (sizeof (struct delayed_comp_unit_disposal));
4649 head->next = delayed_comp_unit_disposal_list;
4650 head->filename = comp_handle->cfile;
4651 comp_handle->cfile = NULL;
4652 delayed_comp_unit_disposal_list = head;
4653 }
4654#endif
4655} 4480}
4656 4481
4657 4482
@@ -4663,7 +4488,6 @@ dispose_comp_unit (struct Lisp_Native_Comp_Unit *comp_handle, bool delay)
4663 loaded the compiler and its dependencies. */ 4488 loaded the compiler and its dependencies. */
4664static Lisp_Object delayed_sources; 4489static Lisp_Object delayed_sources;
4665 4490
4666
4667/* Queue an asyncronous compilation for the source file defining 4491/* Queue an asyncronous compilation for the source file defining
4668 FUNCTION_NAME and perform a late load. 4492 FUNCTION_NAME and perform a late load.
4669 4493
@@ -4922,12 +4746,6 @@ load_comp_unit (struct Lisp_Native_Comp_Unit *comp_u, bool loading_dump,
4922 d_vec_len = XFIXNUM (Flength (comp_u->data_impure_vec)); 4746 d_vec_len = XFIXNUM (Flength (comp_u->data_impure_vec));
4923 for (EMACS_INT i = 0; i < d_vec_len; i++) 4747 for (EMACS_INT i = 0; i < d_vec_len; i++)
4924 data_imp_relocs[i] = AREF (comp_u->data_impure_vec, i); 4748 data_imp_relocs[i] = AREF (comp_u->data_impure_vec, i);
4925
4926 /* If we register them while dumping we will get some entries in
4927 the hash table that will be duplicated when pdumper calls
4928 load_comp_unit. */
4929 if (!will_dump_p ())
4930 register_native_comp_unit (comp_u_lisp_obj);
4931 } 4749 }
4932 4750
4933 if (!loading_dump) 4751 if (!loading_dump)
@@ -4968,6 +4786,8 @@ load_comp_unit (struct Lisp_Native_Comp_Unit *comp_u, bool loading_dump,
4968 /* Clean-up the load ongoing flag in case. */ 4786 /* Clean-up the load ongoing flag in case. */
4969 unbind_to (count, Qnil); 4787 unbind_to (count, Qnil);
4970 4788
4789 register_native_comp_unit (comp_u_lisp_obj);
4790
4971 return; 4791 return;
4972} 4792}
4973 4793
@@ -5110,9 +4930,6 @@ DEFUN ("native-elisp-load", Fnative_elisp_load, Snative_elisp_load, 1, 2, 0,
5110 if (!comp_u->handle) 4930 if (!comp_u->handle)
5111 xsignal2 (Qnative_lisp_load_failed, file, build_string (dynlib_error ())); 4931 xsignal2 (Qnative_lisp_load_failed, file, build_string (dynlib_error ()));
5112 comp_u->file = file; 4932 comp_u->file = file;
5113#ifdef WINDOWSNT
5114 comp_u->cfile = xlispstrdup (file);
5115#endif
5116 comp_u->data_vec = Qnil; 4933 comp_u->data_vec = Qnil;
5117 comp_u->lambda_gc_guard_h = CALLN (Fmake_hash_table, QCtest, Qeq); 4934 comp_u->lambda_gc_guard_h = CALLN (Fmake_hash_table, QCtest, Qeq);
5118 comp_u->lambda_c_name_idx_h = CALLN (Fmake_hash_table, QCtest, Qequal); 4935 comp_u->lambda_c_name_idx_h = CALLN (Fmake_hash_table, QCtest, Qequal);
@@ -5275,10 +5092,9 @@ native compiled one. */);
5275 staticpro (&loadsearch_re_list); 5092 staticpro (&loadsearch_re_list);
5276 loadsearch_re_list = Qnil; 5093 loadsearch_re_list = Qnil;
5277 5094
5278#ifdef WINDOWSNT
5279 staticpro (&all_loaded_comp_units_h); 5095 staticpro (&all_loaded_comp_units_h);
5280 all_loaded_comp_units_h = CALLN (Fmake_hash_table, QCweakness, Qvalue); 5096 all_loaded_comp_units_h =
5281#endif 5097 CALLN (Fmake_hash_table, QCweakness, Qkey_and_value, QCtest, Qequal);
5282 5098
5283 DEFVAR_LISP ("comp-ctxt", Vcomp_ctxt, 5099 DEFVAR_LISP ("comp-ctxt", Vcomp_ctxt,
5284 doc: /* The compiler context. */); 5100 doc: /* The compiler context. */);
diff --git a/src/comp.h b/src/comp.h
index 9270f8bf664..5c7bed6a304 100644
--- a/src/comp.h
+++ b/src/comp.h
@@ -54,13 +54,6 @@ struct Lisp_Native_Comp_Unit
54 bool loaded_once; 54 bool loaded_once;
55 bool load_ongoing; 55 bool load_ongoing;
56 dynlib_handle_ptr handle; 56 dynlib_handle_ptr handle;
57#ifdef WINDOWSNT
58 /* We need to store a copy of the original file name in memory that
59 is not subject to GC because the function to dispose native
60 compilation units is called by the GC. By that time the `file'
61 string may have been sweeped. */
62 char *cfile;
63#endif
64} GCALIGNED_STRUCT; 57} GCALIGNED_STRUCT;
65 58
66#ifdef HAVE_NATIVE_COMP 59#ifdef HAVE_NATIVE_COMP
@@ -92,14 +85,7 @@ extern void syms_of_comp (void);
92extern void maybe_defer_native_compilation (Lisp_Object function_name, 85extern void maybe_defer_native_compilation (Lisp_Object function_name,
93 Lisp_Object definition); 86 Lisp_Object definition);
94 87
95extern void dispose_comp_unit (struct Lisp_Native_Comp_Unit * comp_unit, 88extern void eln_load_path_final_clean_up (void);
96 bool delay);
97
98extern void finish_delayed_disposal_of_comp_units (void);
99
100extern void dispose_all_remaining_comp_units (void);
101
102extern void clean_package_user_dir_of_old_comp_units (void);
103 89
104extern void fixup_eln_load_path (Lisp_Object directory); 90extern void fixup_eln_load_path (Lisp_Object directory);
105 91
@@ -112,24 +98,6 @@ maybe_defer_native_compilation (Lisp_Object function_name,
112 98
113extern void syms_of_comp (void); 99extern void syms_of_comp (void);
114 100
115static inline void
116dispose_comp_unit (struct Lisp_Native_Comp_Unit * comp_handle, bool delay)
117{
118 eassert (false);
119}
120
121static inline void
122dispose_all_remaining_comp_units (void)
123{}
124
125static inline void
126clean_package_user_dir_of_old_comp_units (void)
127{}
128
129static inline void
130finish_delayed_disposal_of_comp_units (void)
131{}
132
133#endif /* #ifdef HAVE_NATIVE_COMP */ 101#endif /* #ifdef HAVE_NATIVE_COMP */
134 102
135#endif /* #ifndef COMP_H */ 103#endif /* #ifndef COMP_H */
diff --git a/src/emacs.c b/src/emacs.c
index 8e52da75926..07e40fdc8bd 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -2407,10 +2407,8 @@ all of which are called before Emacs is actually killed. */
2407 unlink (SSDATA (listfile)); 2407 unlink (SSDATA (listfile));
2408 } 2408 }
2409 2409
2410#if defined (HAVE_NATIVE_COMP) && defined (WINDOWSNT) 2410#ifdef HAVE_NATIVE_COMP
2411 finish_delayed_disposal_of_comp_units (); 2411 eln_load_path_final_clean_up ();
2412 dispose_all_remaining_comp_units ();
2413 clean_package_user_dir_of_old_comp_units ();
2414#endif 2412#endif
2415 2413
2416 if (FIXNUMP (arg)) 2414 if (FIXNUMP (arg))
diff --git a/src/pdumper.c b/src/pdumper.c
index 9c615a9a1a7..da5e7a17363 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -5275,9 +5275,6 @@ dump_do_dump_relocation (const uintptr_t dump_base,
5275 concat2 (Vinvocation_directory, 5275 concat2 (Vinvocation_directory,
5276 installation_state == INSTALLED 5276 installation_state == INSTALLED
5277 ? XCAR (comp_u->file) : XCDR (comp_u->file)); 5277 ? XCAR (comp_u->file) : XCDR (comp_u->file));
5278#ifdef WINDOWSNT
5279 comp_u->cfile = xlispstrdup (comp_u->file);
5280#endif
5281 comp_u->handle = dynlib_open (SSDATA (comp_u->file)); 5278 comp_u->handle = dynlib_open (SSDATA (comp_u->file));
5282 if (!comp_u->handle) 5279 if (!comp_u->handle)
5283 error ("%s", dynlib_error ()); 5280 error ("%s", dynlib_error ());