* fix webconn shutdown race

* make sure writePump returns promptly if readPump returns first

* fix app shutdown race

* minor improvement
Этот коммит содержится в:
Chris
2017-10-16 14:02:33 -07:00
коммит произвёл GitHub
родитель 868bd76f40
Коммит ce314425d1
3 изменённых файлов: 31 добавлений и 7 удалений

Просмотреть файл

@@ -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)