102 lines
2.2 KiB
Go
102 lines
2.2 KiB
Go
|
// Copyright 2018 The Periph Authors. All rights reserved.
|
||
|
// Use of this source code is governed under the Apache License, Version 2.0
|
||
|
// that can be found in the LICENSE file.
|
||
|
|
||
|
// This file contains the parallelized driver loading logic. It is meant to be
|
||
|
// load the drivers as fast as possible by parallelising work.
|
||
|
|
||
|
// +build !tinygo
|
||
|
|
||
|
package periph
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"strconv"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
func initImpl() (*State, error) {
|
||
|
state = &State{}
|
||
|
// At this point, byName is guaranteed to be immutable.
|
||
|
cD := make(chan Driver)
|
||
|
cS := make(chan DriverFailure)
|
||
|
cE := make(chan DriverFailure)
|
||
|
var wg sync.WaitGroup
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
for d := range cD {
|
||
|
state.Loaded = insertDriver(state.Loaded, d)
|
||
|
}
|
||
|
}()
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
for f := range cS {
|
||
|
state.Skipped = insertDriverFailure(state.Skipped, f)
|
||
|
}
|
||
|
}()
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
for f := range cE {
|
||
|
state.Failed = insertDriverFailure(state.Failed, f)
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
stages, err := explodeStages()
|
||
|
if err != nil {
|
||
|
return state, err
|
||
|
}
|
||
|
loaded := make(map[string]struct{}, len(byName))
|
||
|
for _, s := range stages {
|
||
|
s.loadParallel(loaded, cD, cS, cE)
|
||
|
}
|
||
|
close(cD)
|
||
|
close(cS)
|
||
|
close(cE)
|
||
|
wg.Wait()
|
||
|
return state, nil
|
||
|
}
|
||
|
|
||
|
// loadParallel loads all the drivers for this stage in parallel.
|
||
|
//
|
||
|
// Updates loaded in a safe way.
|
||
|
func (s *stage) loadParallel(loaded map[string]struct{}, cD chan<- Driver, cS, cE chan<- DriverFailure) {
|
||
|
success := make(chan string)
|
||
|
go func() {
|
||
|
defer close(success)
|
||
|
wg := sync.WaitGroup{}
|
||
|
loop:
|
||
|
for name, drv := range s.drvs {
|
||
|
// Intentionally do not look at After(), only Prerequisites().
|
||
|
for _, dep := range drv.Prerequisites() {
|
||
|
if _, ok := loaded[dep]; !ok {
|
||
|
cS <- DriverFailure{drv, errors.New("dependency not loaded: " + strconv.Quote(dep))}
|
||
|
continue loop
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Not skipped driver, attempt loading in a goroutine.
|
||
|
wg.Add(1)
|
||
|
go func(n string, d Driver) {
|
||
|
defer wg.Done()
|
||
|
if ok, err := d.Init(); ok {
|
||
|
if err == nil {
|
||
|
cD <- d
|
||
|
success <- n
|
||
|
return
|
||
|
}
|
||
|
cE <- DriverFailure{d, err}
|
||
|
} else {
|
||
|
cS <- DriverFailure{d, err}
|
||
|
}
|
||
|
}(name, drv)
|
||
|
}
|
||
|
wg.Wait()
|
||
|
}()
|
||
|
for s := range success {
|
||
|
loaded[s] = struct{}{}
|
||
|
}
|
||
|
}
|