-
Notifications
You must be signed in to change notification settings - Fork 18k
cmd/go: GOCACHEPROG asks to overwrite existing output ID with open file #71059
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It seems like cache.GetMmap doesn't return the file handle, so it is effectively leaked, AFAICT. Furthermore, it seems like mmap.Mmap leaks the file handle if an error occurs. It is calling mmapFile, which doesn't close the file, so the file remains open. The handle isn't returned on any error path, so callers cannot do the cleanup either. |
FWIW replacing both |
Thanks for the report. Marking as a release blocker since this feature is new(ly promoted out of experimental phase) in Go 1.24, so this needs to be understood and either fixed or explicitly decided to handle it another way. CC @bradfitz, @matloob, @samthanawalla. |
There seem to be a couple of problems here. The unfortunate part is that affects opening module index files on Windows, even when not using a GOCACHEPROG. We'd (pretty much) always fail to open the module index file using GetMmap, and then silently fail to write a new file. We should not be trying to write a new file unless the error we get from GetMmap is a IsNotExist error. If there was a problem mmapping the file (or there was another problem but the file exists on disk), we should just give up then. But the bigger problem, and why things were broken on Windows is the check in GetMmap that the size of the slice returned by mmap is the same as the expected size of the file. Currently on Windows, the size that the Mmap function that GetMmap uses is actually the size of the mapping, which seems to be a multiple of the page size. If I understand this correctly, I think the solution here is to change the Mmap function we use to re-slice the slice we use for the mapping on Windowsto the size of the file before we do the check. Alternatively, we could avoid the size check when we return from the mmap function. |
Change https://go.dev/cl/640155 mentions this issue: |
I can confirm this also fixes the issue. Tried to see if modifying the on-disk content would bring it back in this state, but it just seems to break the build. I personally think this takes it off the "release blocker" status, but seems like some hardening could be a good idea. |
I agree. I'm trying to figure out where that should be done. We need to distinguish the error case where the file isn't cached (in which case we haven't opened the file) vs. the case where the file is the wrong size (in which case we have opened the file, and we won't be able to open it to write the replacement). |
Change https://go.dev/cl/640577 mentions this issue: |
Change https://go.dev/cl/640755 mentions this issue: |
For #71059 Change-Id: I4bbdd14d416dc2e6dae3549a84c16dbef9d4e645 Reviewed-on: https://go-review.googlesource.com/c/go/+/640755 Reviewed-by: Sam Thanawalla <[email protected]> Reviewed-by: Austin Clements <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
On Windows, we can't open a file that's already been opened. Before this change, we'd try to write an index entry if mmapping the entry failed. But that could happen either if the file doesn't exist or if there was a problem mmapping an already opened file. Pass through information about whether the file was actually opened so that we don't try to write to an already opened file. For #71059 Change-Id: I6adabe1093fed9ec37e7fafb13384c102786cbce Reviewed-on: https://go-review.googlesource.com/c/go/+/640577 Reviewed-by: Sam Thanawalla <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
Change https://go.dev/cl/641375 mentions this issue: |
The backslashes on the windows paths will be escaped, so when checking for them in the regular expression we'd have to have quadruple backslashes '\\\\'. Since it's difficult to escape $GOCACHEPROG properly for both json and regexp, just check for a string that ends in cacheprog$GOEXE. We already check that the proper value is reported in go env and go env -changed, and the json test case is mostly useful to verify that GOCACHEPROG shows up in the json output. For #71059 Change-Id: I52d49de61f2309a139f84c4d232b4cd94546ec8c Cq-Include-Trybots: luci.golang.try:gotip-windows-amd64-longtest,gotip-linux-amd64-longtest Reviewed-on: https://go-review.googlesource.com/c/go/+/641375 Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Sam Thanawalla <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
The Mmap function returns a Data struct containing a slice with the mapped contents of the file. Before this change, on Windows, the slice contained the contents of all the pages of the mapping, including past the end of the file. Re-slice the slice to the length of the file (if if the slice is longer) so that the slice contains only the data in the file. For #71059 Change-Id: I389b752505b6fa1252b5c6d836a37bc7e662a45d Reviewed-on: https://go-review.googlesource.com/c/go/+/640155 Reviewed-by: Russ Cox <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
Go version
go version go1.24rc1 windows/amd64
Output of
go env
in your module/workspace:Also
set GOCACHEPROG=go-cacher --verbose
(seems to be missing from go.env)What did you do?
λ go build -v .
with above cache enabled.What did you see happen?
It seems like the go builder keeps the output file open in some cases while asking the cache to update it... This is a problem on Windows, since it is unable to write to an open file.
Request 1, asks for an output id
{"ID":1,"Command":"get","ActionID":"pBAlwHUzJSDNnEKHSTg/axJSDSKXGl8g+SZj8jK6m58="}
We respond with the output id:
{"ID":1,"OutputID":"/Rw93YonOb5fieSCtjYgTXP1E/KTrUN78aVuoX9f++A=","Size":283,"Time":"2024-12-29T15:19:59.2800213+01:00","DiskPath":"C:\\Users\\klaus\\AppData\\Local\\go-cacher\\o-fd1c3ddd8a2739be5f89e482b636204d73f513f293ad437bf1a56ea17f5ffbe0"}
go
file then opens this file (grabbed through process monitor)It then sends a request to update the same output id:
With deterministic files, this fails since
go.exe
never closed the handle on the file:We return an error:
{"ID":2,"Err":"rename C:\\Users\\klaus\\AppData\\Local\\go-cacher\\o-fd1c3ddd8a2739be5f89e482b636204d73f513f293ad437bf1a56ea17f5ffbe0.3796057084 C:\\Users\\klaus\\AppData\\Local\\go-cacher\\o-fd1c3ddd8a2739be5f89e482b636204d73f513f293ad437bf1a56ea17f5ffbe0: Access is denied."}
... and
go.exe
now closes the file:It seems to only happen on non-clean setup. Building from scratch doesn't trigger the issue.
What did you expect to see?
Go closes the file before it asks cache to update it.
The text was updated successfully, but these errors were encountered: