Skip to content

Commit 1908316

Browse files
committed
Limit max goroutine number in per method call
1 parent 3b40eae commit 1908316

File tree

2 files changed

+36
-13
lines changed

2 files changed

+36
-13
lines changed

git.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"time"
1111
)
1212

13-
const _VERSION = "0.2.5"
13+
const _VERSION = "0.2.6"
1414

1515
func Version() string {
1616
return _VERSION

tree_entry.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"sort"
1212
"strconv"
1313
"strings"
14+
"time"
1415
)
1516

1617
type 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
120121
func (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

Comments
 (0)