@@ -1887,6 +1887,8 @@ func (s *Server) Shutdown() error {
1887
1887
//
1888
1888
// ShutdownWithContext does not close keepalive connections so it's recommended to set ReadTimeout and IdleTimeout
1889
1889
// to something else than 0.
1890
+ //
1891
+ // When ShutdownWithContext returns errors, any operation to the Server is unavailable.
1890
1892
func (s * Server ) ShutdownWithContext (ctx context.Context ) (err error ) {
1891
1893
s .mu .Lock ()
1892
1894
defer s .mu .Unlock ()
@@ -1898,11 +1900,7 @@ func (s *Server) ShutdownWithContext(ctx context.Context) (err error) {
1898
1900
return nil
1899
1901
}
1900
1902
1901
- for _ , ln := range s .ln {
1902
- if err = ln .Close (); err != nil {
1903
- return err
1904
- }
1905
- }
1903
+ lnerr := s .closeListenersLocked ()
1906
1904
1907
1905
if s .done != nil {
1908
1906
close (s .done )
@@ -1913,28 +1911,25 @@ func (s *Server) ShutdownWithContext(ctx context.Context) (err error) {
1913
1911
// Now we just have to wait until all workers are done or timeout.
1914
1912
ticker := time .NewTicker (time .Millisecond * 100 )
1915
1913
defer ticker .Stop ()
1916
- END:
1914
+
1917
1915
for {
1918
1916
s .closeIdleConns ()
1919
1917
1920
1918
if open := atomic .LoadInt32 (& s .open ); open == 0 {
1921
- break
1919
+ // There may be a pending request to call ctx.Done(). Therefore, we only set it to nil when open == 0.
1920
+ s .done = nil
1921
+ return lnerr
1922
1922
}
1923
1923
// This is not an optimal solution but using a sync.WaitGroup
1924
1924
// here causes data races as it's hard to prevent Add() to be called
1925
1925
// while Wait() is waiting.
1926
1926
select {
1927
1927
case <- ctx .Done ():
1928
- err = ctx .Err ()
1929
- break END
1928
+ return ctx .Err ()
1930
1929
case <- ticker .C :
1931
1930
continue
1932
1931
}
1933
1932
}
1934
-
1935
- s .done = nil
1936
- s .ln = nil
1937
- return err
1938
1933
}
1939
1934
1940
1935
func acceptConn (s * Server , ln net.Listener , lastPerIPErrorTime * time.Time ) (net.Conn , error ) {
@@ -2749,15 +2744,7 @@ func (ctx *RequestCtx) Deadline() (deadline time.Time, ok bool) {
2749
2744
// Note: Because creating a new channel for every request is just too expensive, so
2750
2745
// RequestCtx.s.done is only closed when the server is shutting down.
2751
2746
func (ctx * RequestCtx ) Done () <- chan struct {} {
2752
- // fix use new variables to prevent panic caused by modifying the original done chan to nil.
2753
- done := ctx .s .done
2754
-
2755
- if done == nil {
2756
- done = make (chan struct {}, 1 )
2757
- done <- struct {}{}
2758
- return done
2759
- }
2760
- return done
2747
+ return ctx .s .done
2761
2748
}
2762
2749
2763
2750
// Err returns a non-nil error value after Done is closed,
@@ -2934,6 +2921,17 @@ func (s *Server) closeIdleConns() {
2934
2921
s .idleConnsMu .Unlock ()
2935
2922
}
2936
2923
2924
+ func (s * Server ) closeListenersLocked () error {
2925
+ var err error
2926
+ for _ , ln := range s .ln {
2927
+ if cerr := ln .Close (); cerr != nil && err == nil {
2928
+ err = cerr
2929
+ }
2930
+ }
2931
+ s .ln = nil
2932
+ return err
2933
+ }
2934
+
2937
2935
// A ConnState represents the state of a client connection to a server.
2938
2936
// It's used by the optional Server.ConnState hook.
2939
2937
type ConnState int
0 commit comments