Skip to content

Commit 20c2bdf

Browse files
authored
Ensure BlameReaders close at end of request (#12102) (#12103)
Backport #12102 this was thought to be due to timeouts, however on closer look this appears to be due to the Close() function of the BlameReader hanging with a blocked stdout pipe. This PR fixes this Close function to: * Cancel the context of the cmd * Close the StdoutReader - ensuring that the output pipe is closed Further it makes the context of the `git blame` command a child of the request context - ensuring that even if Close() is not called, on cancellation of the Request the blame is command will also be cancelled. Fixes #11716 Closes #11727 Signed-off-by: Andrew Thornton <[email protected]>
1 parent df13fc8 commit 20c2bdf

File tree

3 files changed

+19
-8
lines changed

3 files changed

+19
-8
lines changed

modules/git/blame.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
7979
// Close BlameReader - don't run NextPart after invoking that
8080
func (r *BlameReader) Close() error {
8181
defer process.GetManager().Remove(r.pid)
82-
defer r.cancel()
82+
r.cancel()
83+
84+
_ = r.output.Close()
8385

8486
if err := r.cmd.Wait(); err != nil {
8587
return fmt.Errorf("Wait: %v", err)
@@ -89,19 +91,19 @@ func (r *BlameReader) Close() error {
8991
}
9092

9193
// CreateBlameReader creates reader for given repository, commit and file
92-
func CreateBlameReader(repoPath, commitID, file string) (*BlameReader, error) {
94+
func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*BlameReader, error) {
9395
gitRepo, err := OpenRepository(repoPath)
9496
if err != nil {
9597
return nil, err
9698
}
9799
gitRepo.Close()
98100

99-
return createBlameReader(repoPath, GitExecutable, "blame", commitID, "--porcelain", "--", file)
101+
return createBlameReader(ctx, repoPath, GitExecutable, "blame", commitID, "--porcelain", "--", file)
100102
}
101103

102-
func createBlameReader(dir string, command ...string) (*BlameReader, error) {
103-
// FIXME: graceful: This should have a timeout
104-
ctx, cancel := context.WithCancel(DefaultContext)
104+
func createBlameReader(ctx context.Context, dir string, command ...string) (*BlameReader, error) {
105+
// Here we use the provided context - this should be tied to the request performing the blame so that it does not hang around.
106+
ctx, cancel := context.WithCancel(ctx)
105107
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
106108
cmd.Dir = dir
107109
cmd.Stderr = os.Stderr

modules/git/blame_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package git
66

77
import (
8+
"context"
89
"io/ioutil"
910
"testing"
1011

@@ -93,8 +94,10 @@ func TestReadingBlameOutput(t *testing.T) {
9394
if _, err = tempFile.WriteString(exampleBlame); err != nil {
9495
panic(err)
9596
}
97+
ctx, cancel := context.WithCancel(context.Background())
98+
defer cancel()
9699

97-
blameReader, err := createBlameReader("", "cat", tempFile.Name())
100+
blameReader, err := createBlameReader(ctx, "", "cat", tempFile.Name())
98101
if err != nil {
99102
panic(err)
100103
}

routers/repo/blame.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,13 @@ func RefBlame(ctx *context.Context) {
141141
ctx.Data["FileSize"] = blob.Size()
142142
ctx.Data["FileName"] = blob.Name()
143143

144-
blameReader, err := git.CreateBlameReader(models.RepoPath(userName, repoName), commitID, fileName)
144+
ctx.Data["NumLines"], err = blob.GetBlobLineCount()
145+
if err != nil {
146+
ctx.NotFound("GetBlobLineCount", err)
147+
return
148+
}
149+
150+
blameReader, err := git.CreateBlameReader(ctx.Req.Context(), models.RepoPath(userName, repoName), commitID, fileName)
145151
if err != nil {
146152
ctx.NotFound("CreateBlameReader", err)
147153
return

0 commit comments

Comments
 (0)