From e29c33e449bfca6b9481da4e9c2e5a2d40881dec Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Thu, 13 Apr 2023 12:53:58 +0400 Subject: [PATCH] cli/node: Fix deadlock produced by instant RPC service start If `StartWhenSynchronized` is unset in config, `node` command runs RPC service instantly. Previously there was a ground for deadlock. Command started RPC server synchronously. According to server implementation, it sends all internal failures to the parameterized error channel. Deadlock occured because main routine didn't scan the channel. Run `rpcsrv.Server.Start` in a separate go-routine in `startServer`. This prevents potential deadlock caused by writing into unread channel. Fixes #2896. Signed-off-by: Leonard Lyubich --- cli/server/server.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/server/server.go b/cli/server/server.go index bcea6b115..030cacafb 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -491,7 +491,10 @@ func startServer(ctx *cli.Context) error { go serv.Start(errChan) if !cfg.ApplicationConfiguration.RPC.StartWhenSynchronized { - rpcServer.Start() + // Run RPC server in a separate routine. This is necessary to avoid a potential + // deadlock: Start() can write errors to errChan which is not yet read in the + // current execution context (see for-loop below). + go rpcServer.Start() } sigCh := make(chan os.Signal, 1) @@ -546,7 +549,8 @@ Main: rpcServer = rpcsrv.New(chain, cfgnew.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan) serv.AddService(&rpcServer) if !cfgnew.ApplicationConfiguration.RPC.StartWhenSynchronized || serv.IsInSync() { - rpcServer.Start() + // Here similar to the initial run (see above for-loop), so async. + go rpcServer.Start() } pprof.ShutDown() pprof = metrics.NewPprofService(cfgnew.ApplicationConfiguration.Pprof, log)