Fix nob_needs_rebuild with open files in windows - #246
Conversation
|
Could you also please provide a minimal example that reproduces the problem you are having. |
|
Yes, for sure. As I said, this has been an issue when hot-reloading, so to mimic that I just open the file for reading. #define NOB_IMPLEMENTATION
#include "nob.h"
int main(int argc, char **argv) {
NOB_GO_REBUILD_URSELF(argc, argv);
const char *this_file = __FILE__;
const char *file = "output.txt";
const char *contents = "This is an example file\n";
if (nob_file_exists(file)) {
// Mimics running an exe
FILE *f = fopen(file, "r");
}
if (nob_needs_rebuild1(file, this_file)) {
nob_log(NOB_INFO, "rebuilding...");
nob_write_entire_file(file, contents, strlen(contents));
} else {
nob_log(NOB_INFO, "no rebuild necessary");
}
}This works fine in linux: $ clang-21 -o nob nob.c
$ ./nob
[INFO] rebuilding...
$ ./nob
[INFO] no rebuild necessaryBut in windows it errors and always tries to rebuild: PS > clang -o nob.exe nob.c
PS > ./nob.exe
[INFO] rebuilding...
PS > ./nob.exe
[ERROR] Could not open file output.txt: The process cannot access the file because it is being used by another process.
[INFO] rebuilding...With my change, the output is consistent in both platforms: PS > clang -o nob.exe nob.c
PS > ./nob.exe
[INFO] rebuilding...
PS > ./nob.exe
[INFO] no rebuild necessaryIn the real use-case one of my build targets would already be open when nob runs (the program that is executing) but it wouldn't need rebuild, so nob could just skip it, while rebuilding anything else that needs to be rebuilt (the shared library that is hot-reloaded). |
|
I believe I have a better fix here: #272 Note the solution in this PR doesn't fix the sharing exclusive problem I describe in mine. Second, although the MSDN doc you cite is generally correct, and in fact it might work on many Windows implementations, the official documentation for This is actually really more a sharing permission problem, and I believe my PR should fix your error as well. Note how the error says "because it is being used by another process." --Fart |
|
That's surprising to me, it makes me wonder what kind of metadata setting |
|
Agreed, it's surprising. Honestly I kept looking into it / thinking about it and it's highly possible that the documentation note about GetFileTime is an MSDN errata and passing in 0 would work generally with GetFileTime. There's no fundamental reason I can think of for it needing GENERIC_READ. But maybe they messed it up on some Windows implementations and hide it behind that, who knows. If all else / just to be sure, one can also use What I was getting at before I now realize was thrown off by this MSDN comment that had me keep the GENERIC_READ, and I'd like to fix that a bit. I was saying earlier that the sharing problem is separate in a sense but is the more invasive/"fundamental" problem here. But if one connects the dots of the MSDN documentation, then actually the "CreateFile(.., 0, 0, ..)" case is specially designed to not to trigger the sharing problem. The reason this works is all spread out through the CreateFileA docs and I'm on my phone so piecing it together here with links is a bit much, but eventually a logic like this becomes clear: 0, 0 ends up with no "real" file accesses from the handle, and only "real" file accesses trigger validating the exclusive share, and crucially 0, 0 is basically implied from other doc statements to specially not cause CreateFileA to validate the exclusive share initially either. Horrifying. So adding the other share permissions like in my PR would be technically redundant unless you want to both trust MSDN's GENERIC_READ requirement and use GetFileTime. The "ideal" best fix is actually this PR's: with both 0'd + not trusting MSDN + using GetFileTime. Otherwise you can either use GetFileTime but pass GENERIC_READ & the share permissions like in my PR to be safe. Or you can use 0,0 + the fatter GetFileInformationByHandle You can also use All of these should fix it in my books, unless you think one should heed MSDN, then choose accordingly. --Fart |
Windows sucks
But anyway, I need to check if an exe needs_rebuild while it is running because of hot-reload reasons, and it won't work with GENERIC_READ.
As per https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea changing that to 0 works for querying the modification time.
So yeah, accept this if you want.