3232 errUnknownLockOption = errors .New ("invalid lock options" )
3333)
3434
35+ type RouteAdder interface {
36+ AddRoute (handler * common.HTTPHandler , lock * sync.RWMutex , base , endpoint string , loggingWriter io.Writer ) error
37+ }
38+
3539// Server maintains the HTTP router
3640type Server struct {
3741 // log this server writes to
@@ -96,25 +100,34 @@ func (s *Server) DispatchTLS(certFile, keyFile string) error {
96100 return http .ServeTLS (listener , s .handler , certFile , keyFile )
97101}
98102
99- // RegisterChain registers the API endpoints associated with this chain That is,
100- // add <route, handler> pairs to server so that http calls can be made to the vm
101- func (s * Server ) RegisterChain (chainName string , ctx * snow.Context , vmIntf interface {}) {
102- vm , ok := vmIntf .(common.VM )
103- if ! ok {
104- return
105- }
103+ // RegisterChain registers the API endpoints associated with this chain. That is,
104+ // add <route, handler> pairs to server so that API calls can be made to the VM.
105+ // This method runs in a goroutine to avoid a deadlock in the event that the caller
106+ // holds the engine's context lock. Namely, this could happen when the P-Chain is
107+ // creating a new chain and holds the P-Chain's lock when this function is held,
108+ // and at the same time the server's lock is held due to an API call and is trying
109+ // to grab the P-Chain's lock.
110+ func (s * Server ) RegisterChain (chainName string , ctx * snow.Context , engine common.Engine ) {
111+ go s .registerChain (chainName , ctx , engine )
112+ }
113+
114+ func (s * Server ) registerChain (chainName string , ctx * snow.Context , engine common.Engine ) {
115+ var (
116+ handlers map [string ]* common.HTTPHandler
117+ err error
118+ )
106119
107120 ctx .Lock .Lock ()
108- handlers , err := vm .CreateHandlers ()
121+ handlers , err = engine . GetVM () .CreateHandlers ()
109122 ctx .Lock .Unlock ()
110123 if err != nil {
111- s .log .Error ("Failed to create %s handlers: %s" , chainName , err )
124+ s .log .Error ("failed to create %s handlers: %s" , chainName , err )
112125 return
113126 }
114127
115128 httpLogger , err := s .factory .MakeChain (chainName , "http" )
116129 if err != nil {
117- s .log .Error ("Failed to create new http logger: %s" , err )
130+ s .log .Error ("failed to create new http logger: %s" , err )
118131 return
119132 }
120133
@@ -123,15 +136,15 @@ func (s *Server) RegisterChain(chainName string, ctx *snow.Context, vmIntf inter
123136 defaultEndpoint := "bc/" + ctx .ChainID .String ()
124137
125138 // Register each endpoint
126- for extension , service := range handlers {
139+ for extension , handler := range handlers {
127140 // Validate that the route being added is valid
128141 // e.g. "/foo" and "" are ok but "\n" is not
129142 _ , err := url .ParseRequestURI (extension )
130143 if extension != "" && err != nil {
131144 s .log .Error ("could not add route to chain's API handler because route is malformed: %s" , err )
132145 continue
133146 }
134- if err := s .AddChainRoute (service , ctx , defaultEndpoint , extension , httpLogger ); err != nil {
147+ if err := s .AddChainRoute (handler , ctx , defaultEndpoint , extension , httpLogger ); err != nil {
135148 s .log .Error ("error adding route: %s" , err )
136149 }
137150 }
0 commit comments