From 6e990f39de2a8e15636ed9c48ce8139a9322b4d7 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 50ba57d46..6b87e1766 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -491,7 +491,10 @@ func startServer(ctx *cli.Context) error { go serv.Start() 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)