Change:
Mentioned in , there are two places where rustc sets and resets . Doing this means that running multiple compilations in parallel will stomp on each others' environment variables. This can happen, e.g. in rustdoc which runs tests by driving rustc directly. The proper way to set the environment for subprocesses is through the process Command type. cc
Yes, we could do that for subprocesses, but what about syntax extension dylibs? I guess this depends on why we are munging PATH/DYLDLIBRARYPATH/LDLIBRARYPATH: Is this because we want the OS to find the extension dylib itself? If so, we could just search along the augmented path ourselves, then load by absolute path. Or, is this because we need the OS to be able to find other dylibs that the extension dylib depends on? In this case, I don't see another way but to munge the PATH envvar. I don't know about Linux, but Windows does not seem to have a concept of a per-thread dll search path... looks like you wrote the relevant bit of code in Do you remember which of the two it was?
For the case 2, I suppose we could add with an internal mutex, that would temporarily alter PATH before delegating to . Of course, this would still race with direct calls to , but better than nothing. In fact, because essentially allows to mutate global variables, perhaps it should be marked as unsafe?
I thought it was case 1. Interesting idea, marking as unsafe... though note that the rules about when code needs to be annotated with is really meant to ensure data race freedom in safe code, not arbitrary race freedom
Triage: In , mentions this block of code: It looks like this code still doesn't prevent adding to the path over and over again.
I believe we've been seeing this a lot recently as , which is spurious test failures from something being too big, and it's probably environment variables ( coming back to haunt us)

Discussion:
// dependent dlls. Note that this uses cfg!(windows) as opposed to // targ_cfg because syntax extensions are always loaded for the host // compiler, not for the target. <del> let mut _old_path = OsString::new(); </del> <ins> // // This is somewhat of an inherently racy operation, however, as // multiple threads calling this function could possibly continue // extending PATH far beyond what it should. To solve this for now we // just don't add any new elements to PATH which are already there // within PATH. This is basically a targeted fix at #17360 for rustdoc // which runs rustc in parallel but has been seen (#33844) to cause // problems with PATH becoming too long. let mut old_path = OsString::new(); </ins> if cfg!(windows) { <del> _old_path = env::var_os("PATH").unwrap_or(_old_path); </del> <ins> old_path = env::var_os("PATH").unwrap_or(old_path); </ins> let mut new_path = sess.host_filesearch(PathKind::All) .get_dylib_search_paths(); <del> new_path.extend(env::split_paths(&_old_path)); </del> <ins> for path in env::split_paths(&old_path) { if !new_path.contains(&path) { new_path.push(path); } } </ins> env::set_var("PATH", &env::join_paths(new_path).unwrap()); } let features = sess.features.borrow();

syntax_exts, krate); if cfg!(windows) { <del> env::set_var("PATH", &_old_path); </del> <ins> env::set_var("PATH", &old_path); </ins> } *sess.available_macros.borrow_mut() = macro_names; ret