@@ -11,6 +11,7 @@ import (
11
11
"sort"
12
12
"strconv"
13
13
"strings"
14
+ "time"
14
15
)
15
16
16
17
type EntryMode int
@@ -116,18 +117,47 @@ type commitInfo struct {
116
117
117
118
// GetCommitsInfo takes advantages of concurrey to speed up getting information
118
119
// 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
120
121
func (tes Entries ) GetCommitsInfo (commit * Commit , treePath string ) ([][]interface {}, error ) {
121
122
if len (tes ) == 0 {
122
123
return nil , nil
123
124
}
124
125
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 )
125
130
revChan := make (chan commitInfo , 10 )
131
+ doneChan := make (chan error )
126
132
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.
127
135
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
+
128
153
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
+
129
158
if tes [i ].Type != OBJECT_COMMIT {
130
159
go func (i int ) {
160
+ time .Sleep (200 * time .Millisecond )
131
161
cinfo := commitInfo {entryName : tes [i ].Name ()}
132
162
c , err := commit .GetCommitByPath (filepath .Join (treePath , tes [i ].Name ()))
133
163
if err != nil {
@@ -136,6 +166,7 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interfac
136
166
cinfo .infos = []interface {}{tes [i ], c }
137
167
}
138
168
revChan <- cinfo
169
+ <- taskChan // Clear one slot from taskChan to allow new goroutines to start.
139
170
}(i )
140
171
continue
141
172
}
@@ -162,20 +193,12 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interfac
162
193
cinfo .infos = []interface {}{tes [i ], NewSubModuleFile (c , smUrl , tes [i ].ID .String ())}
163
194
}
164
195
revChan <- cinfo
196
+ <- taskChan
165
197
}(i )
166
198
}
167
199
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
179
202
}
180
203
181
204
commitsInfo := make ([][]interface {}, len (tes ))
0 commit comments