mirror of https://github.com/XTLS/Xray-core
Add mutex to injection reolution
- Turns out we already support async DI resolution regardless of feature ordering Previous code contain a race condition causing some resolution is lost - Note that the new mutex cover s.pendingResolutions and s.features but must not cover callbackResolution() due to deadlock - Refactor some method names and simplify codepull/4206/head
parent
66363bc7e3
commit
9c62017e96
98
core/xray.go
98
core/xray.go
|
@ -44,22 +44,13 @@ func getFeature(allFeatures []features.Feature, t reflect.Type) features.Feature
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolution) resolve(allFeatures []features.Feature) (bool, error) {
|
func (r *resolution) callbackResolution(allFeatures []features.Feature) error {
|
||||||
var fs []features.Feature
|
|
||||||
for _, d := range r.deps {
|
|
||||||
f := getFeature(allFeatures, d)
|
|
||||||
if f == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
fs = append(fs, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback := reflect.ValueOf(r.callback)
|
callback := reflect.ValueOf(r.callback)
|
||||||
var input []reflect.Value
|
var input []reflect.Value
|
||||||
callbackType := callback.Type()
|
callbackType := callback.Type()
|
||||||
for i := 0; i < callbackType.NumIn(); i++ {
|
for i := 0; i < callbackType.NumIn(); i++ {
|
||||||
pt := callbackType.In(i)
|
pt := callbackType.In(i)
|
||||||
for _, f := range fs {
|
for _, f := range allFeatures {
|
||||||
if reflect.TypeOf(f).AssignableTo(pt) {
|
if reflect.TypeOf(f).AssignableTo(pt) {
|
||||||
input = append(input, reflect.ValueOf(f))
|
input = append(input, reflect.ValueOf(f))
|
||||||
break
|
break
|
||||||
|
@ -84,15 +75,16 @@ func (r *resolution) resolve(allFeatures []features.Feature) (bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance combines all Xray features.
|
// Instance combines all Xray features.
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
access sync.Mutex
|
statusLock sync.Mutex
|
||||||
features []features.Feature
|
features []features.Feature
|
||||||
featureResolutions []resolution
|
pendingResolutions []resolution
|
||||||
running bool
|
running bool
|
||||||
|
resolveLock sync.Mutex
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
@ -227,9 +219,12 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
||||||
}(),
|
}(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if server.featureResolutions != nil {
|
server.resolveLock.Lock()
|
||||||
|
if server.pendingResolutions != nil {
|
||||||
|
server.resolveLock.Unlock()
|
||||||
return true, errors.New("not all dependencies are resolved.")
|
return true, errors.New("not all dependencies are resolved.")
|
||||||
}
|
}
|
||||||
|
server.resolveLock.Unlock()
|
||||||
|
|
||||||
if err := addInboundHandlers(server, config.Inbound); err != nil {
|
if err := addInboundHandlers(server, config.Inbound); err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
|
@ -248,8 +243,8 @@ func (s *Instance) Type() interface{} {
|
||||||
|
|
||||||
// Close shutdown the Xray instance.
|
// Close shutdown the Xray instance.
|
||||||
func (s *Instance) Close() error {
|
func (s *Instance) Close() error {
|
||||||
s.access.Lock()
|
s.statusLock.Lock()
|
||||||
defer s.access.Unlock()
|
defer s.statusLock.Unlock()
|
||||||
|
|
||||||
s.running = false
|
s.running = false
|
||||||
|
|
||||||
|
@ -283,17 +278,28 @@ func (s *Instance) RequireFeatures(callback interface{}) error {
|
||||||
deps: featureTypes,
|
deps: featureTypes,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
}
|
}
|
||||||
if finished, err := r.resolve(s.features); finished {
|
|
||||||
return err
|
s.resolveLock.Lock()
|
||||||
|
foundAll := true
|
||||||
|
for _, d := range r.deps {
|
||||||
|
f := getFeature(s.features, d)
|
||||||
|
if f == nil {
|
||||||
|
foundAll = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
s.featureResolutions = append(s.featureResolutions, r)
|
}
|
||||||
|
if foundAll {
|
||||||
|
s.resolveLock.Unlock()
|
||||||
|
return r.callbackResolution(s.features)
|
||||||
|
} else {
|
||||||
|
s.pendingResolutions = append(s.pendingResolutions, r)
|
||||||
|
s.resolveLock.Unlock()
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFeature registers a feature into current Instance.
|
// AddFeature registers a feature into current Instance.
|
||||||
func (s *Instance) AddFeature(feature features.Feature) error {
|
func (s *Instance) AddFeature(feature features.Feature) error {
|
||||||
s.features = append(s.features, feature)
|
|
||||||
|
|
||||||
if s.running {
|
if s.running {
|
||||||
if err := feature.Start(); err != nil {
|
if err := feature.Start(); err != nil {
|
||||||
errors.LogInfoInner(s.ctx, err, "failed to start feature")
|
errors.LogInfoInner(s.ctx, err, "failed to start feature")
|
||||||
|
@ -301,27 +307,37 @@ func (s *Instance) AddFeature(feature features.Feature) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.featureResolutions == nil {
|
s.resolveLock.Lock()
|
||||||
|
s.features = append(s.features, feature)
|
||||||
|
if s.pendingResolutions == nil {
|
||||||
|
s.resolveLock.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
var pending []resolution
|
||||||
|
var availableResolution []resolution
|
||||||
|
for _, r := range s.pendingResolutions {
|
||||||
|
foundAll := true
|
||||||
|
for _, d := range r.deps {
|
||||||
|
f := getFeature(s.features, d)
|
||||||
|
if f == nil {
|
||||||
|
foundAll = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundAll {
|
||||||
|
availableResolution = append(availableResolution, r)
|
||||||
|
} else {
|
||||||
|
pending = append(pending, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.pendingResolutions = pending
|
||||||
|
s.resolveLock.Unlock()
|
||||||
|
|
||||||
var pendingResolutions []resolution
|
var err error
|
||||||
for _, r := range s.featureResolutions {
|
for _, r := range availableResolution {
|
||||||
finished, err := r.resolve(s.features)
|
err = r.callbackResolution(s.features) // only return the last error for now
|
||||||
if finished && err != nil {
|
}
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
if !finished {
|
|
||||||
pendingResolutions = append(pendingResolutions, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(pendingResolutions) == 0 {
|
|
||||||
s.featureResolutions = nil
|
|
||||||
} else if len(pendingResolutions) < len(s.featureResolutions) {
|
|
||||||
s.featureResolutions = pendingResolutions
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeature returns a feature of the given type, or nil if such feature is not registered.
|
// GetFeature returns a feature of the given type, or nil if such feature is not registered.
|
||||||
|
@ -334,8 +350,8 @@ func (s *Instance) GetFeature(featureType interface{}) features.Feature {
|
||||||
//
|
//
|
||||||
// xray:api:stable
|
// xray:api:stable
|
||||||
func (s *Instance) Start() error {
|
func (s *Instance) Start() error {
|
||||||
s.access.Lock()
|
s.statusLock.Lock()
|
||||||
defer s.access.Unlock()
|
defer s.statusLock.Unlock()
|
||||||
|
|
||||||
s.running = true
|
s.running = true
|
||||||
for _, f := range s.features {
|
for _, f := range s.features {
|
||||||
|
|
Loading…
Reference in New Issue