99 "fmt"
1010 "io"
1111 "os"
12+ "path/filepath"
13+ "time"
1214
1315 "code.gitea.io/gitea/modules/log"
1416)
@@ -102,7 +104,7 @@ type CheckAttributeReader struct {
102104
103105 stdinReader io.ReadCloser
104106 stdinWriter * os.File
105- stdOut attributeWriter
107+ stdOut * nulSeparatedAttributeWriter
106108 cmd * Command
107109 env []string
108110 ctx context.Context
@@ -152,7 +154,6 @@ func (c *CheckAttributeReader) Init(ctx context.Context) error {
152154 return nil
153155}
154156
155- // Run run cmd
156157func (c * CheckAttributeReader ) Run () error {
157158 defer func () {
158159 _ = c .stdinReader .Close ()
@@ -176,7 +177,7 @@ func (c *CheckAttributeReader) Run() error {
176177func (c * CheckAttributeReader ) CheckPath (path string ) (rs map [string ]string , err error ) {
177178 defer func () {
178179 if err != nil && err != c .ctx .Err () {
179- log .Error ("Unexpected error when checking path %s in %s. Error : %v" , path , c .Repo .Path , err )
180+ log .Error ("Unexpected error when checking path %s in %s, error : %v" , path , filepath . Base ( c .Repo .Path ) , err )
180181 }
181182 }()
182183
@@ -191,9 +192,31 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
191192 return nil , err
192193 }
193194
195+ reportTimeout := func () error {
196+ stdOutClosed := false
197+ select {
198+ case <- c .stdOut .closed :
199+ stdOutClosed = true
200+ default :
201+ }
202+ debugMsg := fmt .Sprintf ("check path %q in repo %q" , path , filepath .Base (c .Repo .Path ))
203+ debugMsg += fmt .Sprintf (", stdOut: tmp=%q, pos=%d, closed=%v" , string (c .stdOut .tmp ), c .stdOut .pos , stdOutClosed )
204+ if c .cmd .cmd != nil {
205+ debugMsg += fmt .Sprintf (", process state: %q" , c .cmd .cmd .ProcessState .String ())
206+ }
207+ _ = c .Close ()
208+ return fmt .Errorf ("CheckPath timeout: %s" , debugMsg )
209+ }
210+
194211 rs = make (map [string ]string )
195212 for range c .Attributes {
196213 select {
214+ case <- time .After (5 * time .Second ):
215+ // There is a strange "hang" problem in gitdiff.GetDiff -> CheckPath
216+ // So add a timeout here to mitigate the problem, and output more logs for debug purpose
217+ // In real world, if CheckPath runs long than seconds, it blocks the end user's operation,
218+ // and at the moment the CheckPath result is not so important, so we can just ignore it.
219+ return nil , reportTimeout ()
197220 case attr , ok := <- c .stdOut .ReadAttribute ():
198221 if ! ok {
199222 return nil , c .ctx .Err ()
@@ -206,18 +229,12 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
206229 return rs , nil
207230}
208231
209- // Close close pip after use
210232func (c * CheckAttributeReader ) Close () error {
211233 c .cancel ()
212234 err := c .stdinWriter .Close ()
213235 return err
214236}
215237
216- type attributeWriter interface {
217- io.WriteCloser
218- ReadAttribute () <- chan attributeTriple
219- }
220-
221238type attributeTriple struct {
222239 Filename string
223240 Attribute string
@@ -281,7 +298,7 @@ func (wr *nulSeparatedAttributeWriter) Close() error {
281298 return nil
282299}
283300
284- // Create a check attribute reader for the current repository and provided commit ID
301+ // CheckAttributeReader creates a check attribute reader for the current repository and provided commit ID
285302func (repo * Repository ) CheckAttributeReader (commitID string ) (* CheckAttributeReader , context.CancelFunc ) {
286303 indexFilename , worktree , deleteTemporaryFile , err := repo .ReadTreeToTemporaryIndex (commitID )
287304 if err != nil {
@@ -303,21 +320,21 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
303320 }
304321 ctx , cancel := context .WithCancel (repo .Ctx )
305322 if err := checker .Init (ctx ); err != nil {
306- log .Error ("Unable to open checker for %s. Error : %v" , commitID , err )
323+ log .Error ("Unable to open attribute checker for commit %s, error : %v" , commitID , err )
307324 } else {
308325 go func () {
309326 err := checker .Run ()
310- if err != nil && err != ctx . Err ( ) {
311- log .Error ("Unable to open checker for %s. Error : %v" , commitID , err )
327+ if err != nil && ! IsErrCanceledOrKilled ( err ) {
328+ log .Error ("Attribute checker for commit %s exits with error : %v" , commitID , err )
312329 }
313330 cancel ()
314331 }()
315332 }
316- deferable := func () {
333+ deferrable := func () {
317334 _ = checker .Close ()
318335 cancel ()
319336 deleteTemporaryFile ()
320337 }
321338
322- return checker , deferable
339+ return checker , deferrable
323340}
0 commit comments