Skip to content

Commit c5e7c0d

Browse files
committed
fix: ListenAndServe hangs up in case of errors in http.Serve
fix: Shutdown does not call in case of same errors
1 parent 741b483 commit c5e7c0d

File tree

2 files changed

+52
-21
lines changed

2 files changed

+52
-21
lines changed

app.go

+36-15
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ func (a *App) Shutdown() {
114114
go func(s *http.Server) {
115115
defer wg.Done()
116116
err := s.Shutdown(context.Background())
117-
logger.Printf("ZeroDT: server '%v' is shutdown with: '%v'", s.Addr, err)
117+
if err != nil {
118+
logger.Printf("ZeroDT: server %s has been shutdown with: %v", s.Addr, err)
119+
return
120+
}
121+
logger.Printf("ZeroDT: server %s has been shutdown", s.Addr)
118122
}(s)
119123
}
120124

@@ -126,7 +130,7 @@ func (a *App) Shutdown() {
126130
// the inherited ones. It also serves the servers and monitors OS
127131
// signals.
128132
func (a *App) ListenAndServe() error {
129-
inherited, m, err := inherit()
133+
inherited, messenger, err := inherit()
130134
if err != nil {
131135
logger.Printf("ZeroDT: failed to inherit listeners with: '%v'", err)
132136
return err
@@ -155,45 +159,53 @@ func (a *App) ListenAndServe() error {
155159
for _, s := range a.servers {
156160
go func(s *http.Server) {
157161
defer srvWG.Done()
162+
// Make sure Shutdown is not blocked event if
163+
// notifyListener.Accept() not call.
164+
servedOnce := &doneOnce{wg: &a.served}
165+
defer servedOnce.Done()
166+
// Make sure startWG.Wait() is not blocked in case of error
167+
// in acquireOrCreateListener.
168+
startOnce := &doneOnce{wg: &startWG}
169+
defer startOnce.Done()
170+
158171
l, err := e.acquireOrCreateListener("tcp", s.Addr)
159-
startWG.Done()
160172
if err != nil {
161173
// TODO: error channel
162-
logger.Printf("ZeroDT: failed to listen on '%v' with %v", s.Addr, err)
174+
logger.Printf("ZeroDT: failed to listen on %v with %v", s.Addr, err)
163175
return
164176
}
177+
// A server is about to Serve and already listen.
178+
startOnce.Done()
179+
// Wait for parent to start if set.
165180
parentWG.Wait()
166181
if finalErr != nil {
167-
logger.Printf("ZeroDT: server '%v' has finished serving with %v", s.Addr, err)
182+
logger.Printf("ZeroDT: server %v has finished serving with %v", s.Addr, err)
168183
return
169184
}
170-
err = s.Serve(&notifyListener{Listener: tcpKeepAliveListener{l}, wg: &a.served})
171-
logger.Printf("ZeroDT: server '%v' has finished serving with %v", s.Addr, err)
185+
err = s.Serve(&notifyListener{Listener: tcpKeepAliveListener{l}, doneOnce: servedOnce})
186+
logger.Printf("ZeroDT: server %v has finished serving with %v", s.Addr, err)
172187
}(s)
173188
}
174189

175190
// Wait for all listeners to start listening.
176191
startWG.Wait()
177192

178-
if m != nil {
179-
finalErr = protocolActAsChild(m, a.waitChildTimeout, a.waitParentShutdownTimeout, a.PreParentExitFn)
193+
if messenger != nil {
194+
finalErr = protocolActAsChild(messenger, a.waitChildTimeout, a.waitParentShutdownTimeout, a.PreParentExitFn)
180195
}
181-
182196
if finalErr == nil {
183197
a.PreServeFn(e.didInherit())
184198
}
185199

186200
// Allow serverse's goroutines to start serving.
187201
parentWG.Done()
188-
189-
// Wait for all server's. They may fail or be stopped by
190-
// calling 'Shutdown'.
202+
// Wait for all server's. They may fail or be stopped by calling
203+
// Shutdown.
191204
srvWG.Wait()
192-
193205
// Stop handling OS signals and wait for it's goroutine.
194206
sigCancelFunc()
195207
sigWG.Wait()
196-
208+
logger.Printf("ZeroDT: exit")
197209
return finalErr
198210
}
199211

@@ -205,11 +217,18 @@ func (a *App) handleSignals(ctx context.Context, wg *sync.WaitGroup, e *exchange
205217
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT, syscall.SIGUSR2)
206218
defer signal.Stop(signals)
207219

220+
wasShutdown := false
221+
208222
CatchSignals:
209223
for {
210224
select {
211225
// Exit.
212226
case <-ctx.Done():
227+
if !wasShutdown {
228+
// Possbile in case of errors in 'http.Serve'.
229+
// It's needed to start shutdown process any way.
230+
a.Shutdown()
231+
}
213232
return
214233
// OS signal.
215234
case s := <-signals:
@@ -218,6 +237,7 @@ CatchSignals:
218237
// Shutdown servers. No exit here.
219238
case syscall.SIGINT, syscall.SIGTERM:
220239
a.Shutdown()
240+
wasShutdown = true
221241
// Fork/Exec a child and shutdown.
222242
case syscall.SIGUSR2:
223243
_, f, err := forkExec(e.activeFiles())
@@ -233,6 +253,7 @@ CatchSignals:
233253
// Nothing to do with errors.
234254
protocolActAsParent(m, a.waitChildTimeout, a.waitParentShutdownTimeout, func() {
235255
a.Shutdown()
256+
wasShutdown = true
236257
})
237258
}
238259
}

notify_listener.go

+16-6
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,32 @@ import (
2323
//
2424
// var wg sync.WaitGroup
2525
// l, _ := net.Listen("tcp", ":8080")
26+
// once := &doneOnce(wg: &wg)
2627
//
27-
// go s.Serve(&notifyListener{Listener: l, wg: &wg})
28+
// go s.Serve(&notifyListener{Listener: l, doneOnce: once})
2829
//
2930
// wg.Wait()
3031
// // It's safe to call shutdown here
3132
// s.Shutdown(context.Background())
3233
//
33-
type notifyListener struct {
34-
net.Listener
34+
35+
type doneOnce struct {
3536
wg *sync.WaitGroup
3637
once sync.Once
3738
}
3839

39-
func (l *notifyListener) Accept() (net.Conn, error) {
40-
l.once.Do(func() {
41-
l.wg.Done()
40+
func (n *doneOnce) Done() {
41+
n.once.Do(func() {
42+
n.wg.Done()
4243
})
44+
}
45+
46+
type notifyListener struct {
47+
net.Listener
48+
doneOnce *doneOnce
49+
}
50+
51+
func (l *notifyListener) Accept() (net.Conn, error) {
52+
l.doneOnce.Done()
4353
return l.Listener.Accept()
4454
}

0 commit comments

Comments
 (0)