@@ -11,6 +11,7 @@ import (
1111 "sort"
1212 "strconv"
1313 "strings"
14+ "time"
1415)
1516
1617type EntryMode int
@@ -116,18 +117,47 @@ type commitInfo struct {
116117
117118// GetCommitsInfo takes advantages of concurrey to speed up getting information
118119// of all commits that are corresponding to these entries.
119- // TODO: limit max goroutines at same time
120+ // TODO: limit max goroutines number should be configurable
120121func (tes Entries ) GetCommitsInfo (commit * Commit , treePath string ) ([][]interface {}, error ) {
121122 if len (tes ) == 0 {
122123 return nil , nil
123124 }
124125
126+ // Length of taskChan determines how many goroutines (subprocesses) can run at the same time.
127+ // The length of revChan should be same as taskChan so goroutines whoever finished job can
128+ // exit as early as possible, only store data inside channel.
129+ taskChan := make (chan bool , 10 )
125130 revChan := make (chan commitInfo , 10 )
131+ doneChan := make (chan error )
126132
133+ // Receive loop will exit when it collects same number of data pieces as tree entries.
134+ // It notifies doneChan before exits or notify early with possible error.
127135 infoMap := make (map [string ][]interface {}, len (tes ))
136+ go func () {
137+ i := 0
138+ for info := range revChan {
139+ if info .err != nil {
140+ doneChan <- info .err
141+ return
142+ }
143+
144+ infoMap [info .entryName ] = info .infos
145+ i ++
146+ if i == len (tes ) {
147+ break
148+ }
149+ }
150+ doneChan <- nil
151+ }()
152+
128153 for i := range tes {
154+ // When taskChan is idle (or has empty slots), put operation will not block.
155+ // However when taskChan is full, code will block and wait any running goroutines to finish.
156+ taskChan <- true
157+
129158 if tes [i ].Type != OBJECT_COMMIT {
130159 go func (i int ) {
160+ time .Sleep (200 * time .Millisecond )
131161 cinfo := commitInfo {entryName : tes [i ].Name ()}
132162 c , err := commit .GetCommitByPath (filepath .Join (treePath , tes [i ].Name ()))
133163 if err != nil {
@@ -136,6 +166,7 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interfac
136166 cinfo .infos = []interface {}{tes [i ], c }
137167 }
138168 revChan <- cinfo
169+ <- taskChan // Clear one slot from taskChan to allow new goroutines to start.
139170 }(i )
140171 continue
141172 }
@@ -162,20 +193,12 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interfac
162193 cinfo .infos = []interface {}{tes [i ], NewSubModuleFile (c , smUrl , tes [i ].ID .String ())}
163194 }
164195 revChan <- cinfo
196+ <- taskChan
165197 }(i )
166198 }
167199
168- i := 0
169- for info := range revChan {
170- if info .err != nil {
171- return nil , info .err
172- }
173-
174- infoMap [info .entryName ] = info .infos
175- i ++
176- if i == len (tes ) {
177- break
178- }
200+ if err := <- doneChan ; err != nil {
201+ return nil , err
179202 }
180203
181204 commitsInfo := make ([][]interface {}, len (tes ))
0 commit comments