@@ -13,63 +13,45 @@ import (
1313 "strconv"
1414 "strings"
1515 "sync/atomic"
16- "time"
1716
1817 "code.gitea.io/gitea/modules/git/gitcmd"
1918 "code.gitea.io/gitea/modules/log"
19+ "code.gitea.io/gitea/modules/util"
2020)
2121
22- var catFileBatchDebugWaitClose atomic.Int64
23-
2422type catFileBatchCommunicator struct {
25- closeFunc func (err error )
23+ closeFunc atomic. Pointer [ func (err error )]
2624 reqWriter io.Writer
2725 respReader * bufio.Reader
2826 debugGitCmd * gitcmd.Command
2927}
3028
31- func (b * catFileBatchCommunicator ) Close () {
32- if b .closeFunc != nil {
33- b .closeFunc (nil )
34- b .closeFunc = nil
29+ func (b * catFileBatchCommunicator ) Close (err ... error ) {
30+ if fn := b .closeFunc .Swap (nil ); fn != nil {
31+ (* fn )(util .OptionalArg (err ))
3532 }
3633}
3734
38- // newCatFileBatch opens git cat-file --batch in the provided repo and returns a stdin pipe, a stdout reader and cancel function
39- func newCatFileBatch (ctx context.Context , repoPath string , cmdCatFile * gitcmd.Command ) ( ret * catFileBatchCommunicator ) {
35+ // newCatFileBatch opens git cat-file --batch/--batch-check/--batch-command command and prepares the stdin/ stdout pipes for communication.
36+ func newCatFileBatch (ctx context.Context , repoPath string , cmdCatFile * gitcmd.Command ) * catFileBatchCommunicator {
4037 ctx , ctxCancel := context .WithCancelCause (ctx )
41-
42- // We often want to feed the commits in order into cat-file --batch, followed by their trees and subtrees as necessary.
4338 stdinWriter , stdoutReader , stdPipeClose := cmdCatFile .MakeStdinStdoutPipe ()
44- pipeClose := func () {
45- if delay := catFileBatchDebugWaitClose .Load (); delay > 0 {
46- time .Sleep (time .Duration (delay )) // for testing purpose only
47- }
48- stdPipeClose ()
49- }
50- closeFunc := func (err error ) {
51- ctxCancel (err )
52- pipeClose ()
53- }
54- return newCatFileBatchWithCloseFunc (ctx , repoPath , cmdCatFile , stdinWriter , stdoutReader , closeFunc )
55- }
56-
57- func newCatFileBatchWithCloseFunc (ctx context.Context , repoPath string , cmdCatFile * gitcmd.Command ,
58- stdinWriter gitcmd.PipeWriter , stdoutReader gitcmd.PipeReader , closeFunc func (err error ),
59- ) * catFileBatchCommunicator {
6039 ret := & catFileBatchCommunicator {
6140 debugGitCmd : cmdCatFile ,
62- closeFunc : closeFunc ,
6341 reqWriter : stdinWriter ,
6442 respReader : bufio .NewReaderSize (stdoutReader , 32 * 1024 ), // use a buffered reader for rich operations
6543 }
44+ ret .closeFunc .Store (new (func (err error ) {
45+ ctxCancel (err )
46+ stdPipeClose ()
47+ }))
6648
6749 err := cmdCatFile .WithDir (repoPath ).StartWithStderr (ctx )
6850 if err != nil {
6951 log .Error ("Unable to start git command %v: %v" , cmdCatFile .LogString (), err )
7052 // ideally here it should return the error, but it would require refactoring all callers
7153 // so just return a dummy communicator that does nothing, almost the same behavior as before, not bad
72- closeFunc (err )
54+ ret . Close (err )
7355 return ret
7456 }
7557
@@ -78,12 +60,33 @@ func newCatFileBatchWithCloseFunc(ctx context.Context, repoPath string, cmdCatFi
7860 if err != nil && ! errors .Is (err , context .Canceled ) {
7961 log .Error ("cat-file --batch command failed in repo %s, error: %v" , repoPath , err )
8062 }
81- closeFunc (err )
63+ ret . Close (err )
8264 }()
8365
8466 return ret
8567}
8668
69+ func (b * catFileBatchCommunicator ) debugKill () (ret struct {
70+ beforeClose chan struct {}
71+ blockClose chan struct {}
72+ afterClose chan struct {}
73+ },
74+ ) {
75+ ret .beforeClose = make (chan struct {})
76+ ret .blockClose = make (chan struct {})
77+ ret .afterClose = make (chan struct {})
78+ oldCloseFunc := b .closeFunc .Load ()
79+ b .closeFunc .Store (new (func (err error ) {
80+ b.closeFunc .Store (nil )
81+ close (ret .beforeClose )
82+ < - ret .blockClose
83+ (* oldCloseFunc )(err )
84+ close (ret .afterClose )
85+ }))
86+ b .debugGitCmd .DebugKill ()
87+ return ret
88+ }
89+
8790// catFileBatchParseInfoLine reads the header line from cat-file --batch
8891// We expect: <oid> SP <type> SP <size> LF
8992// then leaving the rest of the stream "<contents> LF" to be read
0 commit comments