Changeset 449 in code


Ignore:
Timestamp:
Feb 9, 2021, 4:34:46 PM (4 years ago)
Author:
contact
Message:

Add support for graceful shutdown

Closes: https://todo.sr.ht/~emersion/soju/45

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/cmd/soju/main.go

    r418 r449  
    88        "net/http"
    99        "net/url"
     10        "os"
     11        "os/signal"
    1012        "strings"
     13        "syscall"
    1114
    1215        "github.com/pires/go-proxyproto"
     
    9093                        ln = proxyProtoListener(ln, srv)
    9194                        go func() {
    92                                 log.Fatal(srv.Serve(ln))
     95                                if err := srv.Serve(ln); err != nil {
     96                                        log.Printf("serving %q: %v", listen, err)
     97                                }
    9398                        }()
    9499                case "irc+insecure":
     
    103108                        ln = proxyProtoListener(ln, srv)
    104109                        go func() {
    105                                 log.Fatal(srv.Serve(ln))
     110                                if err := srv.Serve(ln); err != nil {
     111                                        log.Printf("serving %q: %v", listen, err)
     112                                }
    106113                        }()
    107114                case "wss":
     
    116123                        }
    117124                        go func() {
    118                                 log.Fatal(httpSrv.ListenAndServeTLS("", ""))
     125                                if err := httpSrv.ListenAndServeTLS("", ""); err != nil {
     126                                        log.Fatalf("serving %q: %v", listen, err)
     127                                }
    119128                        }()
    120129                case "ws+insecure":
     
    128137                        }
    129138                        go func() {
    130                                 log.Fatal(httpSrv.ListenAndServe())
     139                                if err := httpSrv.ListenAndServe(); err != nil {
     140                                        log.Fatalf("serving %q: %v", listen, err)
     141                                }
    131142                        }()
    132143                case "ident":
     
    145156                        ln = proxyProtoListener(ln, srv)
    146157                        go func() {
    147                                 log.Fatal(srv.Identd.Serve(ln))
     158                                if err := srv.Identd.Serve(ln); err != nil {
     159                                        log.Printf("serving %q: %v", listen, err)
     160                                }
    148161                        }()
    149162                default:
     
    153166                log.Printf("server listening on %q", listen)
    154167        }
    155         log.Fatal(srv.Run())
     168
     169        sigCh := make(chan os.Signal, 1)
     170        signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
     171
     172        if err := srv.Start(); err != nil {
     173                log.Fatal(err)
     174        }
     175
     176        <-sigCh
     177        log.Print("shutting down server")
     178        srv.Shutdown()
    156179}
    157180
  • trunk/server.go

    r409 r449  
    66        "net"
    77        "net/http"
     8        "strings"
    89        "sync"
    910        "sync/atomic"
     
    5556        Identd         *Identd // can be nil
    5657
    57         db *DB
    58 
    59         lock  sync.Mutex
    60         users map[string]*user
     58        db     *DB
     59        stopWG sync.WaitGroup
     60
     61        lock      sync.Mutex
     62        listeners map[net.Listener]struct{}
     63        users     map[string]*user
    6164}
    6265
     
    6568                Logger:       log.New(log.Writer(), "", log.LstdFlags),
    6669                HistoryLimit: 1000,
     70                db:           db,
     71                listeners:    make(map[net.Listener]struct{}),
    6772                users:        make(map[string]*user),
    68                 db:           db,
    6973        }
    7074}
     
    7478}
    7579
    76 func (s *Server) Run() error {
     80func (s *Server) Start() error {
    7781        users, err := s.db.ListUsers()
    7882        if err != nil {
     
    8690        s.lock.Unlock()
    8791
    88         select {}
     92        return nil
     93}
     94
     95func (s *Server) Shutdown() {
     96        s.lock.Lock()
     97        for ln := range s.listeners {
     98                if err := ln.Close(); err != nil {
     99                        s.Logger.Printf("failed to stop listener: %v", err)
     100                }
     101        }
     102        for _, u := range s.users {
     103                u.events <- eventStop{}
     104        }
     105        s.lock.Unlock()
     106
     107        s.stopWG.Wait()
    89108}
    90109
     
    117136        s.users[u.Username] = u
    118137
     138        s.stopWG.Add(1)
     139
    119140        go func() {
    120141                u.run()
     
    123144                delete(s.users, u.Username)
    124145                s.lock.Unlock()
     146
     147                s.stopWG.Done()
    125148        }()
    126149
     
    146169
    147170func (s *Server) Serve(ln net.Listener) error {
     171        s.lock.Lock()
     172        s.listeners[ln] = struct{}{}
     173        s.lock.Unlock()
     174
     175        s.stopWG.Add(1)
     176
     177        defer func() {
     178                s.lock.Lock()
     179                delete(s.listeners, ln)
     180                s.lock.Unlock()
     181
     182                s.stopWG.Done()
     183        }()
     184
    148185        for {
    149186                conn, err := ln.Accept()
    150                 if err != nil {
     187                // TODO: use net.ErrClosed when available
     188                if err != nil && strings.Contains(err.Error(), "use of closed network connection") {
     189                        return nil
     190                } else if err != nil {
    151191                        return fmt.Errorf("failed to accept connection: %v", err)
    152192                }
Note: See TracChangeset for help on using the changeset viewer.