1. Replacing the current executable is generally okay on Unix and Unix-likes, but it's not particularly well-defined. Linux handles it gracefully, others may ETXTBSY or similar depending on how the replacement is done.
2. The hack being used on Windows is awful: it involves patching the underlying C runtime, can fail in uncontrolled ways (including in an `atexit` handler), and is fundamentally racy. The state it leaves a failed operation in isn't self-healing, and surfaces confusing filesystem state to the user on failure.
If you want to replace a running executable on Windows, either ask the user (and have them affirmatively kill the running program) or do the "cooperative rename" trick[1]. It doesn't require any unsafety, much less patching the runtime underneath you.
On #1, totally agree, and security systems can add an extra winkle...
With code-signed binaries on a Mac, macOS Gatekeeper caches signatures by inode. If the binary is replaced/re-written in a way which retains the old inode, and you try to execute it, the OS will SIGKILL the process immediately. Not fun to debug the first time you encounter it!
Can you please clarify how this "patches the underlying C runtime"? What this crate is doing is not particularly out of the ordinary. You will find similar code in quite a few self updating windows executables.
> or do the "cooperative rename" trick[1]
I'm not entirely sure how this is much different? This is pretty much what this is doing. It spawns a copy of itself (with the exe being marked for self deletion) and then deletes the other executable.
> What this crate is doing is not particularly out of the ordinary.
"Out of the ordinary" and "good" are different qualifiers: patching a runtime initialization table might be ordinary (and even acceptable) in a leaf executable where the dependencies are carefully checked for conflicting initialization behavior, but it's pretty dangerous in a library (or crate) that might get mixed into all kinds of executables with different initialization expectations.
> This is pretty much what this is doing.
It's close, but you're relying on much racier and flimsier primitives. The trick that I'm most familiar with in Win32 applications is to create a named pipe or other IPC mechanism to communicate over, since the failure modes there are much better defined (and not subject to race conditions).
> patching a runtime initialization table might be ordinary (and even acceptable) in a leaf executable where the dependencies are carefully checked for conflicting initialization behavior, but it's pretty dangerous in a library (or crate) that might get mixed into all kinds of executables with different initialization expectations.
I'm not sure why that would be the case. Any C++ static constructor ends up in that table. Any Rust crate using #[ctor] ends up in there.
> It's close, but you're relying on much racier and flimsier primitives. The trick that I'm most familiar with in Win32 applications is to create a named pipe or other IPC mechanism to communicate over, since the failure modes there are much better defined (and not subject to race conditions).
As opposed to WaitSingleObject? Or what exactly are you objecting to here exactly? I'm also not sure which race condition you are referring to in particular. The main race this has (or any other library that does something similar) is that it needs a utility process to spawn to inherit the handle of the copy gc process. No named pipe will resolve this.
Thanks for the breakdown of why this is a bad idea. I was considering using this for a Windows Rust project I maintain, but now I know better. Ideally tradeoffs like these should be mentioned clearly in the readme.
1. Replacing the current executable is generally okay on Unix and Unix-likes, but it's not particularly well-defined. Linux handles it gracefully, others may ETXTBSY or similar depending on how the replacement is done.
2. The hack being used on Windows is awful: it involves patching the underlying C runtime, can fail in uncontrolled ways (including in an `atexit` handler), and is fundamentally racy. The state it leaves a failed operation in isn't self-healing, and surfaces confusing filesystem state to the user on failure.
If you want to replace a running executable on Windows, either ask the user (and have them affirmatively kill the running program) or do the "cooperative rename" trick[1]. It doesn't require any unsafety, much less patching the runtime underneath you.
[1]: https://social.msdn.microsoft.com/Forums/vstudio/en-US/07fb6...