@@ -34,6 +34,24 @@ type Searcher struct {
3434 doneCh chan empty
3535}
3636
37+ // Wrapper around searcher - used for sending the searcher struct to channel once
38+ // successfully returned from `newSearcher` function.
39+ // We need this wrapper because we need to have the `name` of a repo which a
40+ // particular searcher belongs to.
41+ type searcherWrapper struct {
42+ searcher * Searcher
43+ name string
44+ }
45+
46+ // Wrapper around error - used for sending error to channel once returned from
47+ // `newSearcher` function.
48+ // We need this wrapper because we need to have the `name` of a repo which a
49+ // particular error belongs to.
50+ type errorWrapper struct {
51+ error error
52+ name string
53+ }
54+
3755type empty struct {}
3856type limiter chan bool
3957
@@ -277,21 +295,49 @@ func MakeAll(cfg *config.Config) (map[string]*Searcher, map[string]error, error)
277295
278296 lim := makeLimiter (cfg .MaxConcurrentIndexers )
279297
280- for name , repo := range cfg .Repos {
281- s , err := newSearcher (cfg .DbPath , name , repo , refs , lim )
282- if err != nil {
283- log .Print (err )
284- errs [name ] = err
285- continue
286- }
298+ // Channel to receive the successfully created searchers.
299+ searcherCh := make (chan searcherWrapper , 1 )
300+ // Channel to receive the errors in creation of searchers.
301+ errorCh := make (chan errorWrapper , 1 )
302+ // Channel to quit the go routine listening for new successful searchers or errors.
303+ quitCh := make (chan struct {}, 1 )
304+
305+ // Create a wait group for total number of repos so that we can proceed once the
306+ // go routines to create searchers for all repos have returned.
307+ var wg sync.WaitGroup
308+ wg .Add (len (cfg .Repos ))
287309
288- searchers [name ] = s
310+ for name , repo := range cfg .Repos {
311+ go newSearcherConcurrent (cfg .DbPath , name , repo , refs , lim , searcherCh , errorCh )
289312 }
290313
314+ // Create a listener on searcherCh and errorCh.
315+ // It also listens on quitCh to decide when to return.
316+ go func () {
317+ for {
318+ select {
319+ case sw := <- searcherCh :
320+ searchers [sw .name ] = sw .searcher
321+ wg .Done ()
322+ case ew := <- errorCh :
323+ log .Print (ew .error )
324+ errs [ew .name ] = ew .error
325+ wg .Done ()
326+ case <- quitCh :
327+ return
328+ }
329+ }
330+ }()
331+
291332 if err := refs .removeUnclaimed (); err != nil {
292333 return nil , nil , err
293334 }
294335
336+ // Wait for all go routines to finish.
337+ wg .Wait ()
338+ // Send close signal to quitCh to return the listening go routine.
339+ close (quitCh )
340+
295341 // after all the repos are in good shape, we start their polling
296342 for _ , s := range searchers {
297343 s .begin ()
@@ -464,3 +510,33 @@ func newSearcher(
464510
465511 return s , nil
466512}
513+
514+ // This function is a wrapper around `newSearcher` function.
515+ // It respects the parameter `cfg.MaxConcurrentIndexers` while making the
516+ // creation of searchers for various repositories concurrent.
517+ func newSearcherConcurrent (
518+ dbpath , name string ,
519+ repo * config.Repo ,
520+ refs * foundRefs ,
521+ lim limiter ,
522+ searcherCh chan searcherWrapper ,
523+ errorCh chan errorWrapper ) {
524+
525+ // acquire a token from the rate limiter
526+ lim .Acquire ()
527+ defer lim .Release ()
528+
529+ s , err := newSearcher (dbpath , name , repo , refs , lim )
530+ if err != nil {
531+ errorCh <- errorWrapper {
532+ error : err ,
533+ name : name ,
534+ }
535+ return
536+ }
537+
538+ searcherCh <- searcherWrapper {
539+ searcher : s ,
540+ name : name ,
541+ }
542+ }
0 commit comments