@ -22,6 +22,7 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/types/logger"
"tailscale.com/util/execqueue"
"tailscale.com/util/execqueue"
"tailscale.com/util/mak"
"tailscale.com/util/testenv"
"tailscale.com/util/testenv"
)
)
@ -97,7 +98,8 @@ type ExtensionHost struct {
initialized atomic . Bool
initialized atomic . Bool
// activeExtensions is a subset of allExtensions that have been initialized and are ready to use.
// activeExtensions is a subset of allExtensions that have been initialized and are ready to use.
activeExtensions [ ] ipnext . Extension
activeExtensions [ ] ipnext . Extension
// extensionsByName are the activeExtensions indexed by their names.
// extensionsByName are the extensions indexed by their names.
// They are not necessarily initialized (in activeExtensions) yet.
extensionsByName map [ string ] ipnext . Extension
extensionsByName map [ string ] ipnext . Extension
// postInitWorkQueue is a queue of functions to be executed
// postInitWorkQueue is a queue of functions to be executed
// by the workQueue after all extensions have been initialized.
// by the workQueue after all extensions have been initialized.
@ -184,6 +186,24 @@ func newExtensionHost(logf logger.Logf, b Backend, overrideExts ...*ipnext.Defin
return nil , fmt . Errorf ( "failed to create %q extension: %v" , d . Name ( ) , err )
return nil , fmt . Errorf ( "failed to create %q extension: %v" , d . Name ( ) , err )
}
}
host . allExtensions = append ( host . allExtensions , ext )
host . allExtensions = append ( host . allExtensions , ext )
if d . Name ( ) != ext . Name ( ) {
return nil , fmt . Errorf ( "extension name %q does not match the registered name %q" , ext . Name ( ) , d . Name ( ) )
}
if _ , ok := host . extensionsByName [ ext . Name ( ) ] ; ok {
return nil , fmt . Errorf ( "duplicate extension name %q" , ext . Name ( ) )
} else {
mak . Set ( & host . extensionsByName , ext . Name ( ) , ext )
}
typ := reflect . TypeOf ( ext )
if _ , ok := host . extByType . Load ( typ ) ; ok {
if _ , ok := ext . ( interface { PermitDoubleRegister ( ) } ) ; ! ok {
return nil , fmt . Errorf ( "duplicate extension type %T" , ext )
}
}
host . extByType . Store ( typ , ext )
}
}
return host , nil
return host , nil
}
}
@ -215,10 +235,6 @@ func (h *ExtensionHost) init() {
defer h . initDone . Store ( true )
defer h . initDone . Store ( true )
// Initialize the extensions in the order they were registered.
// Initialize the extensions in the order they were registered.
h . mu . Lock ( )
h . activeExtensions = make ( [ ] ipnext . Extension , 0 , len ( h . allExtensions ) )
h . extensionsByName = make ( map [ string ] ipnext . Extension , len ( h . allExtensions ) )
h . mu . Unlock ( )
for _ , ext := range h . allExtensions {
for _ , ext := range h . allExtensions {
// Do not hold the lock while calling [ipnext.Extension.Init].
// Do not hold the lock while calling [ipnext.Extension.Init].
// Extensions call back into the host to register their callbacks,
// Extensions call back into the host to register their callbacks,
@ -240,8 +256,6 @@ func (h *ExtensionHost) init() {
// We'd like to make them visible to other extensions that are initialized later.
// We'd like to make them visible to other extensions that are initialized later.
h . mu . Lock ( )
h . mu . Lock ( )
h . activeExtensions = append ( h . activeExtensions , ext )
h . activeExtensions = append ( h . activeExtensions , ext )
h . extensionsByName [ ext . Name ( ) ] = ext
h . extByType . Store ( reflect . TypeOf ( ext ) , ext )
h . mu . Unlock ( )
h . mu . Unlock ( )
}
}