Fix webconn shutdown race (#7631)
* fix webconn shutdown race * make sure writePump returns promptly if readPump returns first * fix app shutdown race * minor improvement
Этот коммит содержится в:
@@ -46,3 +46,11 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
status = m.Run()
|
status = m.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAppRace(t *testing.T) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
a := New()
|
||||||
|
a.StartServer()
|
||||||
|
a.Shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ type Server struct {
|
|||||||
Router *mux.Router
|
Router *mux.Router
|
||||||
Server *http.Server
|
Server *http.Server
|
||||||
ListenAddr *net.TCPAddr
|
ListenAddr *net.TCPAddr
|
||||||
|
|
||||||
|
didFinishListen chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var allowedMethods []string = []string{
|
var allowedMethods []string = []string{
|
||||||
@@ -179,17 +181,19 @@ func (a *App) StartServer() {
|
|||||||
|
|
||||||
if *utils.Cfg.ServiceSettings.Forward80To443 {
|
if *utils.Cfg.ServiceSettings.Forward80To443 {
|
||||||
go func() {
|
go func() {
|
||||||
listener, err := net.Listen("tcp", ":80")
|
redirectListener, err := net.Listen("tcp", ":80")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
listener.Close()
|
||||||
l4g.Error("Unable to setup forwarding")
|
l4g.Error("Unable to setup forwarding")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer listener.Close()
|
defer redirectListener.Close()
|
||||||
|
|
||||||
http.Serve(listener, http.HandlerFunc(redirectHTTPToHTTPS))
|
http.Serve(redirectListener, http.HandlerFunc(redirectHTTPToHTTPS))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.Srv.didFinishListen = make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
var err error
|
var err error
|
||||||
if *utils.Cfg.ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
|
if *utils.Cfg.ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
|
||||||
@@ -215,6 +219,7 @@ func (a *App) StartServer() {
|
|||||||
l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
|
l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
close(a.Srv.didFinishListen)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,8 +252,18 @@ func (a *App) StopServer() {
|
|||||||
if a.Srv.Server != nil {
|
if a.Srv.Server != nil {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
|
ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := a.Srv.Server.Shutdown(ctx); err != nil {
|
didShutdown := false
|
||||||
l4g.Warn(err.Error())
|
for a.Srv.didFinishListen != nil && !didShutdown {
|
||||||
|
if err := a.Srv.Server.Shutdown(ctx); err != nil {
|
||||||
|
l4g.Warn(err.Error())
|
||||||
|
}
|
||||||
|
timer := time.NewTimer(time.Millisecond * 50)
|
||||||
|
select {
|
||||||
|
case <-a.Srv.didFinishListen:
|
||||||
|
didShutdown = true
|
||||||
|
case <-timer.C:
|
||||||
|
}
|
||||||
|
timer.Stop()
|
||||||
}
|
}
|
||||||
a.Srv.Server.Close()
|
a.Srv.Server.Close()
|
||||||
a.Srv.Server = nil
|
a.Srv.Server = nil
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (a *App) NewWebConn(ws *websocket.Conn, session model.Session, t goi18n.Tra
|
|||||||
UserId: session.UserId,
|
UserId: session.UserId,
|
||||||
T: t,
|
T: t,
|
||||||
Locale: locale,
|
Locale: locale,
|
||||||
endWritePump: make(chan struct{}, 1),
|
endWritePump: make(chan struct{}, 2),
|
||||||
pumpFinished: make(chan struct{}, 1),
|
pumpFinished: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,13 +111,14 @@ func (c *WebConn) Pump() {
|
|||||||
ch <- struct{}{}
|
ch <- struct{}{}
|
||||||
}()
|
}()
|
||||||
c.readPump()
|
c.readPump()
|
||||||
|
c.endWritePump <- struct{}{}
|
||||||
<-ch
|
<-ch
|
||||||
|
c.App.HubUnregister(c)
|
||||||
c.pumpFinished <- struct{}{}
|
c.pumpFinished <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *WebConn) readPump() {
|
func (c *WebConn) readPump() {
|
||||||
defer func() {
|
defer func() {
|
||||||
c.App.HubUnregister(c)
|
|
||||||
c.WebSocket.Close()
|
c.WebSocket.Close()
|
||||||
}()
|
}()
|
||||||
c.WebSocket.SetReadLimit(model.SOCKET_MAX_MESSAGE_SIZE_KB)
|
c.WebSocket.SetReadLimit(model.SOCKET_MAX_MESSAGE_SIZE_KB)
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user