From a8f5449cd95c06e8b55b7f776fc826fc7675c1b6 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 6 Mar 2024 19:50:39 +0800
Subject: [PATCH] Avoid unexpected panic in graceful manager (#29629)

There is a fundamental design problem of the "manager" and the "wait
group".
If nothing has started, the "Wait" just panics: sync: WaitGroup is
reused before previous Wait has returned
There is no clear solution besides a complete rewriting of the "manager"

If there are some mistakes in the app.ini, end users would just see the
"panic", but not the real error messages. A real case: #27643

This PR is just a quick fix for the annoying panic problem.

(cherry picked from commit 90a3f2d4b7ed3890d9655c0334444f86d89b7b30)
---
 modules/graceful/manager_unix.go    | 10 +++++++++-
 modules/graceful/manager_windows.go | 10 +++++++++-
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go
index f4af4993d9..edf5fc248f 100644
--- a/modules/graceful/manager_unix.go
+++ b/modules/graceful/manager_unix.go
@@ -59,7 +59,15 @@ func (g *Manager) start() {
 	go func() {
 		defer close(startupDone)
 		// Wait till we're done getting all the listeners and then close the unused ones
-		g.createServerWaitGroup.Wait()
+		func() {
+			// FIXME: there is a fundamental design problem of the "manager" and the "wait group".
+			// If nothing has started, the "Wait" just panics: sync: WaitGroup is reused before previous Wait has returned
+			// There is no clear solution besides a complete rewriting of the "manager"
+			defer func() {
+				_ = recover()
+			}()
+			g.createServerWaitGroup.Wait()
+		}()
 		// Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function
 		_ = CloseProvidedListeners()
 		g.notify(readyMsg)
diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go
index 0248dcb24d..ecf30af3f3 100644
--- a/modules/graceful/manager_windows.go
+++ b/modules/graceful/manager_windows.go
@@ -150,7 +150,15 @@ func (g *Manager) awaitServer(limit time.Duration) bool {
 	c := make(chan struct{})
 	go func() {
 		defer close(c)
-		g.createServerWaitGroup.Wait()
+		func() {
+			// FIXME: there is a fundamental design problem of the "manager" and the "wait group".
+			// If nothing has started, the "Wait" just panics: sync: WaitGroup is reused before previous Wait has returned
+			// There is no clear solution besides a complete rewriting of the "manager"
+			defer func() {
+				_ = recover()
+			}()
+			g.createServerWaitGroup.Wait()
+		}()
 	}()
 	if limit > 0 {
 		select {