about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-02-03 14:15:22 -0800
committerGitHub <noreply@github.com>2023-02-03 14:15:22 -0800
commit1594b58ce78b772aeb4bc8913f7f0c6ea3e0fcc7 (patch)
tree29c7c3d693002c29ead56dacc73ffc02b3dbcf7f
parente99e05d13592d2e9868fa69f98dc01f9766f05ed (diff)
parent227b2858da8aac6791e86a86f938a652993e5ea3 (diff)
downloadrust-1594b58ce78b772aeb4bc8913f7f0c6ea3e0fcc7.tar.gz
rust-1594b58ce78b772aeb4bc8913f7f0c6ea3e0fcc7.zip
Rollup merge of #107595 - michaelwoerister:retry_proc_macro_loading, r=petrochenkov
Retry opening proc-macro DLLs a few times on Windows.

On Windows, the compiler [sometimes](https://users.rust-lang.org/t/error-loadlibraryexw-failed/77603) fails with the message `error: LoadLibraryExW failed` when trying to load a proc-macro crate. The error seems to occur intermittently, similar to https://github.com/rust-lang/rust/issues/86929, however, it seems to be almost impossible to reproduce outside of CI environments and thus very hard to debug. The fact that the error only occurs intermittently makes me think that this is a timing related issue.

This PR is an attempt to mitigate the issue by letting the compiler retry a few times when encountering this specific error (which resolved the issue described in https://github.com/rust-lang/rust/issues/86929).
-rw-r--r--compiler/rustc_metadata/src/creader.rs42
1 files changed, 40 insertions, 2 deletions
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 653f2b39d3e..44d6c587da3 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -33,6 +33,7 @@ use rustc_target::spec::{PanicStrategy, TargetTriple};
 use proc_macro::bridge::client::ProcMacro;
 use std::ops::Fn;
 use std::path::Path;
+use std::time::Duration;
 use std::{cmp, env};
 
 #[derive(Clone)]
@@ -689,8 +690,7 @@ impl<'a> CrateLoader<'a> {
     ) -> Result<&'static [ProcMacro], CrateError> {
         // Make sure the path contains a / or the linker will search for it.
         let path = env::current_dir().unwrap().join(path);
-        let lib = unsafe { libloading::Library::new(path) }
-            .map_err(|err| CrateError::DlOpen(err.to_string()))?;
+        let lib = load_dylib(&path, 5).map_err(|err| CrateError::DlOpen(err))?;
 
         let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
         let sym = unsafe { lib.get::<*const &[ProcMacro]>(sym_name.as_bytes()) }
@@ -1093,3 +1093,41 @@ fn alloc_error_handler_spans(sess: &Session, krate: &ast::Crate) -> Vec<Span> {
     visit::walk_crate(&mut f, krate);
     f.spans
 }
+
+// On Windows the compiler would sometimes intermittently fail to open the
+// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
+// system still holds a lock on the file, so we retry a few times before calling it
+// an error.
+fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, String> {
+    assert!(max_attempts > 0);
+
+    let mut last_error = None;
+
+    for attempt in 0..max_attempts {
+        match unsafe { libloading::Library::new(&path) } {
+            Ok(lib) => {
+                if attempt > 0 {
+                    debug!(
+                        "Loaded proc-macro `{}` after {} attempts.",
+                        path.display(),
+                        attempt + 1
+                    );
+                }
+                return Ok(lib);
+            }
+            Err(err) => {
+                // Only try to recover from this specific error.
+                if !matches!(err, libloading::Error::LoadLibraryExW { .. }) {
+                    return Err(err.to_string());
+                }
+
+                last_error = Some(err);
+                std::thread::sleep(Duration::from_millis(100));
+                debug!("Failed to load proc-macro `{}`. Retrying.", path.display());
+            }
+        }
+    }
+
+    debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts);
+    Err(format!("{} (retried {} times)", last_error.unwrap(), max_attempts))
+}