Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16481)

* Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end

Fix #16427 (again!)

* handle sharing violation error code

Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
zeripath 2021-07-21 17:34:53 +01:00 committed by GitHub
parent e6c222511d
commit 3299f044d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 3 deletions

View File

@ -7,6 +7,7 @@ package git
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"io" "io"
"math" "math"
"strconv" "strconv"
@ -15,20 +16,24 @@ import (
// CatFileBatch opens git cat-file --batch in the provided repo and returns a stdin pipe, a stdout reader and cancel function // CatFileBatch opens git cat-file --batch in the provided repo and returns a stdin pipe, a stdout reader and cancel function
func CatFileBatch(repoPath string) (*io.PipeWriter, *bufio.Reader, func()) { func CatFileBatch(repoPath string) (*io.PipeWriter, *bufio.Reader, func()) {
// Next feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary. // We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
// so let's create a batch stdin and stdout // so let's create a batch stdin and stdout
batchStdinReader, batchStdinWriter := io.Pipe() batchStdinReader, batchStdinWriter := io.Pipe()
batchStdoutReader, batchStdoutWriter := io.Pipe() batchStdoutReader, batchStdoutWriter := io.Pipe()
ctx, ctxCancel := context.WithCancel(DefaultContext)
closed := make(chan struct{})
cancel := func() { cancel := func() {
_ = batchStdinReader.Close() _ = batchStdinReader.Close()
_ = batchStdinWriter.Close() _ = batchStdinWriter.Close()
_ = batchStdoutReader.Close() _ = batchStdoutReader.Close()
_ = batchStdoutWriter.Close() _ = batchStdoutWriter.Close()
ctxCancel()
<-closed
} }
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := NewCommand("cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader) err := NewCommandContext(ctx, "cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
if err != nil { if err != nil {
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) _ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String())) _ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
@ -36,10 +41,11 @@ func CatFileBatch(repoPath string) (*io.PipeWriter, *bufio.Reader, func()) {
_ = batchStdoutWriter.Close() _ = batchStdoutWriter.Close()
_ = batchStdinReader.Close() _ = batchStdinReader.Close()
} }
close(closed)
}() }()
// For simplicities sake we'll us a buffered reader to read from the cat-file --batch // For simplicities sake we'll us a buffered reader to read from the cat-file --batch
batchReader := bufio.NewReader(batchStdoutReader) batchReader := bufio.NewReaderSize(batchStdoutReader, 32*1024)
return batchStdinWriter, batchReader, cancel return batchStdinWriter, batchReader, cancel
} }

View File

@ -6,10 +6,13 @@ package util
import ( import (
"os" "os"
"runtime"
"syscall" "syscall"
"time" "time"
) )
const windowsSharingViolationError syscall.Errno = 32
// Remove removes the named file or (empty) directory with at most 5 attempts. // Remove removes the named file or (empty) directory with at most 5 attempts.
func Remove(name string) error { func Remove(name string) error {
var err error var err error
@ -25,6 +28,12 @@ func Remove(name string) error {
continue continue
} }
if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
// try again
<-time.After(100 * time.Millisecond)
continue
}
if unwrapped == syscall.ENOENT { if unwrapped == syscall.ENOENT {
// it's already gone // it's already gone
return nil return nil
@ -48,6 +57,12 @@ func RemoveAll(name string) error {
continue continue
} }
if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
// try again
<-time.After(100 * time.Millisecond)
continue
}
if unwrapped == syscall.ENOENT { if unwrapped == syscall.ENOENT {
// it's already gone // it's already gone
return nil return nil
@ -71,6 +86,12 @@ func Rename(oldpath, newpath string) error {
continue continue
} }
if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
// try again
<-time.After(100 * time.Millisecond)
continue
}
if i == 0 && os.IsNotExist(err) { if i == 0 && os.IsNotExist(err) {
return err return err
} }