Browse Source

all: Don't let Suture capture panics (fixes #4758) (#5119)

Fork with new option.
Jakob Borg 7 years ago
parent
commit
48795dba07

+ 4 - 2
cmd/stdiscosrv/main.go

@@ -19,11 +19,11 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/tlsutil"
 	"github.com/syncthing/syncthing/lib/tlsutil"
 	"github.com/syndtr/goleveldb/leveldb/opt"
 	"github.com/syndtr/goleveldb/leveldb/opt"
-	"github.com/thejerf/suture"
 )
 )
 
 
 const (
 const (
@@ -164,7 +164,9 @@ func main() {
 	}
 	}
 
 
 	// Root of the service tree.
 	// Root of the service tree.
-	main := suture.NewSimple("main")
+	main := suture.New("main", suture.Spec{
+		PanicPanics: true,
+	})
 
 
 	// Start the database.
 	// Start the database.
 	db, err := newLevelDBStore(dir)
 	db, err := newLevelDBStore(dir)

+ 7 - 3
cmd/syncthing/gui_test.go

@@ -23,13 +23,13 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/d4l3k/messagediff"
 	"github.com/d4l3k/messagediff"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/fs"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/sync"
-	"github.com/thejerf/suture"
 )
 )
 
 
 func TestCSRFToken(t *testing.T) {
 func TestCSRFToken(t *testing.T) {
@@ -77,7 +77,9 @@ func TestStopAfterBrokenConfig(t *testing.T) {
 	srv := newAPIService(protocol.LocalDeviceID, w, "../../test/h1/https-cert.pem", "../../test/h1/https-key.pem", "", nil, nil, nil, nil, nil, nil, nil, nil)
 	srv := newAPIService(protocol.LocalDeviceID, w, "../../test/h1/https-cert.pem", "../../test/h1/https-key.pem", "", nil, nil, nil, nil, nil, nil, nil, nil)
 	srv.started = make(chan string)
 	srv.started = make(chan string)
 
 
-	sup := suture.NewSimple("test")
+	sup := suture.New("test", suture.Spec{
+		PanicPanics: true,
+	})
 	sup.Add(srv)
 	sup.Add(srv)
 	sup.ServeBackground()
 	sup.ServeBackground()
 
 
@@ -487,7 +489,9 @@ func startHTTP(cfg *mockedConfig) (string, error) {
 	svc.started = addrChan
 	svc.started = addrChan
 
 
 	// Actually start the API service
 	// Actually start the API service
-	supervisor := suture.NewSimple("API test")
+	supervisor := suture.New("API test", suture.Spec{
+		PanicPanics: true,
+	})
 	supervisor.Add(svc)
 	supervisor.Add(svc)
 	supervisor.ServeBackground()
 	supervisor.ServeBackground()
 
 

+ 2 - 1
cmd/syncthing/main.go

@@ -47,7 +47,7 @@ import (
 	"github.com/syncthing/syncthing/lib/tlsutil"
 	"github.com/syncthing/syncthing/lib/tlsutil"
 	"github.com/syncthing/syncthing/lib/upgrade"
 	"github.com/syncthing/syncthing/lib/upgrade"
 
 
-	"github.com/thejerf/suture"
+	"github.com/calmh/suture"
 
 
 	_ "net/http/pprof" // Need to import this to support STPROFILER.
 	_ "net/http/pprof" // Need to import this to support STPROFILER.
 )
 )
@@ -594,6 +594,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
 		Log: func(line string) {
 		Log: func(line string) {
 			l.Debugln(line)
 			l.Debugln(line)
 		},
 		},
+		PanicPanics: true,
 	})
 	})
 	mainService.ServeBackground()
 	mainService.ServeBackground()
 
 

+ 4 - 2
cmd/syncthing/summaryservice.go

@@ -9,10 +9,10 @@ package main
 import (
 import (
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/sync"
-	"github.com/thejerf/suture"
 )
 )
 
 
 // The folderSummaryService adds summary information events (FolderSummary and
 // The folderSummaryService adds summary information events (FolderSummary and
@@ -36,7 +36,9 @@ type folderSummaryService struct {
 
 
 func newFolderSummaryService(cfg configIntf, m modelIntf) *folderSummaryService {
 func newFolderSummaryService(cfg configIntf, m modelIntf) *folderSummaryService {
 	service := &folderSummaryService{
 	service := &folderSummaryService{
-		Supervisor:      suture.NewSimple("folderSummaryService"),
+		Supervisor: suture.New("folderSummaryService", suture.Spec{
+			PanicPanics: true,
+		}),
 		cfg:             cfg,
 		cfg:             cfg,
 		model:           m,
 		model:           m,
 		stop:            make(chan struct{}),
 		stop:            make(chan struct{}),

+ 1 - 1
lib/beacon/beacon.go

@@ -10,7 +10,7 @@ import (
 	"net"
 	"net"
 	stdsync "sync"
 	stdsync "sync"
 
 
-	"github.com/thejerf/suture"
+	"github.com/calmh/suture"
 )
 )
 
 
 type recv struct {
 type recv struct {

+ 2 - 1
lib/beacon/broadcast.go

@@ -11,8 +11,8 @@ import (
 	"net"
 	"net"
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/sync"
-	"github.com/thejerf/suture"
 )
 )
 
 
 type Broadcast struct {
 type Broadcast struct {
@@ -36,6 +36,7 @@ func NewBroadcast(port int) *Broadcast {
 			Log: func(line string) {
 			Log: func(line string) {
 				l.Debugln(line)
 				l.Debugln(line)
 			},
 			},
+			PanicPanics: true,
 		}),
 		}),
 		port:   port,
 		port:   port,
 		inbox:  make(chan []byte),
 		inbox:  make(chan []byte),

+ 2 - 1
lib/beacon/multicast.go

@@ -12,7 +12,7 @@ import (
 	"net"
 	"net"
 	"time"
 	"time"
 
 
-	"github.com/thejerf/suture"
+	"github.com/calmh/suture"
 	"golang.org/x/net/ipv6"
 	"golang.org/x/net/ipv6"
 )
 )
 
 
@@ -36,6 +36,7 @@ func NewMulticast(addr string) *Multicast {
 			Log: func(line string) {
 			Log: func(line string) {
 				l.Debugln(line)
 				l.Debugln(line)
 			},
 			},
+			PanicPanics: true,
 		}),
 		}),
 		inbox:  make(chan []byte),
 		inbox:  make(chan []byte),
 		outbox: make(chan recv, 16),
 		outbox: make(chan recv, 16),

+ 3 - 1
lib/connections/service.go

@@ -30,7 +30,7 @@ import (
 	_ "github.com/syncthing/syncthing/lib/pmp"
 	_ "github.com/syncthing/syncthing/lib/pmp"
 	_ "github.com/syncthing/syncthing/lib/upnp"
 	_ "github.com/syncthing/syncthing/lib/upnp"
 
 
-	"github.com/thejerf/suture"
+	"github.com/calmh/suture"
 	"golang.org/x/time/rate"
 	"golang.org/x/time/rate"
 )
 )
 
 
@@ -105,6 +105,7 @@ func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *
 			Log: func(line string) {
 			Log: func(line string) {
 				l.Infoln(line)
 				l.Infoln(line)
 			},
 			},
+			PanicPanics: true,
 		}),
 		}),
 		cfg:                  cfg,
 		cfg:                  cfg,
 		myID:                 myID,
 		myID:                 myID,
@@ -131,6 +132,7 @@ func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *
 			},
 			},
 			FailureThreshold: 2,
 			FailureThreshold: 2,
 			FailureBackoff:   600 * time.Second,
 			FailureBackoff:   600 * time.Second,
+			PanicPanics:      true,
 		}),
 		}),
 	}
 	}
 	cfg.Subscribe(service)
 	cfg.Subscribe(service)

+ 5 - 3
lib/discover/cache.go

@@ -10,10 +10,10 @@ import (
 	stdsync "sync"
 	stdsync "sync"
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/util"
 	"github.com/syncthing/syncthing/lib/util"
-	"github.com/thejerf/suture"
 )
 )
 
 
 // The CachingMux aggregates results from multiple Finders. Each Finder has
 // The CachingMux aggregates results from multiple Finders. Each Finder has
@@ -51,8 +51,10 @@ type cachedError interface {
 
 
 func NewCachingMux() CachingMux {
 func NewCachingMux() CachingMux {
 	return &cachingMux{
 	return &cachingMux{
-		Supervisor: suture.NewSimple("discover.cachingMux"),
-		mut:        sync.NewRWMutex(),
+		Supervisor: suture.New("discover.cachingMux", suture.Spec{
+			PanicPanics: true,
+		}),
+		mut: sync.NewRWMutex(),
 	}
 	}
 }
 }
 
 

+ 1 - 1
lib/discover/discover.go

@@ -9,8 +9,8 @@ package discover
 import (
 import (
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
-	"github.com/thejerf/suture"
 )
 )
 
 
 // A Finder provides lookup services of some kind.
 // A Finder provides lookup services of some kind.

+ 4 - 2
lib/discover/local.go

@@ -18,11 +18,11 @@ import (
 	"strconv"
 	"strconv"
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/syncthing/syncthing/lib/beacon"
 	"github.com/syncthing/syncthing/lib/beacon"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/events"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/rand"
 	"github.com/syncthing/syncthing/lib/rand"
-	"github.com/thejerf/suture"
 )
 )
 
 
 type localClient struct {
 type localClient struct {
@@ -48,7 +48,9 @@ const (
 
 
 func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) {
 func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) {
 	c := &localClient{
 	c := &localClient{
-		Supervisor:      suture.NewSimple("local"),
+		Supervisor: suture.New("local", suture.Spec{
+			PanicPanics: true,
+		}),
 		myID:            id,
 		myID:            id,
 		addrList:        addrList,
 		addrList:        addrList,
 		localBcastTick:  time.NewTicker(BroadcastInterval).C,
 		localBcastTick:  time.NewTicker(BroadcastInterval).C,

+ 2 - 1
lib/model/model.go

@@ -22,6 +22,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/calmh/suture"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/config"
 	"github.com/syncthing/syncthing/lib/connections"
 	"github.com/syncthing/syncthing/lib/connections"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/db"
@@ -35,7 +36,6 @@ import (
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/upgrade"
 	"github.com/syncthing/syncthing/lib/upgrade"
 	"github.com/syncthing/syncthing/lib/versioner"
 	"github.com/syncthing/syncthing/lib/versioner"
-	"github.com/thejerf/suture"
 )
 )
 
 
 var locationLocal *time.Location
 var locationLocal *time.Location
@@ -136,6 +136,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, clientName, clientVersi
 			Log: func(line string) {
 			Log: func(line string) {
 				l.Debugln(line)
 				l.Debugln(line)
 			},
 			},
+			PanicPanics: true,
 		}),
 		}),
 		cfg:                 cfg,
 		cfg:                 cfg,
 		db:                  ldb,
 		db:                  ldb,

+ 0 - 0
vendor/github.com/thejerf/suture/LICENSE → vendor/github.com/calmh/suture/LICENSE


+ 0 - 0
vendor/github.com/thejerf/suture/doc.go → vendor/github.com/calmh/suture/doc.go


+ 1 - 1
vendor/github.com/thejerf/suture/messages.go → vendor/github.com/calmh/suture/messages.go

@@ -46,7 +46,7 @@ func (s *Supervisor) serviceEnded(id serviceID, complete bool) {
 }
 }
 
 
 type serviceEnded struct {
 type serviceEnded struct {
-	id serviceID
+	id       serviceID
 	complete bool
 	complete bool
 }
 }
 
 

+ 0 - 0
vendor/github.com/thejerf/suture/service.go → vendor/github.com/calmh/suture/service.go


+ 17 - 9
vendor/github.com/thejerf/suture/supervisor.go → vendor/github.com/calmh/suture/supervisor.go

@@ -108,6 +108,7 @@ type Supervisor struct {
 	control              chan supervisorMessage
 	control              chan supervisorMessage
 	liveness             chan struct{}
 	liveness             chan struct{}
 	resumeTimer          <-chan time.Time
 	resumeTimer          <-chan time.Time
+	recoverPanics        bool
 
 
 	LogBadStop BadStopLogger
 	LogBadStop BadStopLogger
 	LogFailure FailureLogger
 	LogFailure FailureLogger
@@ -133,6 +134,7 @@ type Spec struct {
 	LogBadStop       BadStopLogger
 	LogBadStop       BadStopLogger
 	LogFailure       FailureLogger
 	LogFailure       FailureLogger
 	LogBackoff       BackoffLogger
 	LogBackoff       BackoffLogger
+	PanicPanics      bool
 }
 }
 
 
 /*
 /*
@@ -214,6 +216,7 @@ func New(name string, spec Spec) (s *Supervisor) {
 	} else {
 	} else {
 		s.timeout = spec.Timeout
 		s.timeout = spec.Timeout
 	}
 	}
+	s.recoverPanics = !spec.PanicPanics
 
 
 	// overriding these allows for testing the threshold behavior
 	// overriding these allows for testing the threshold behavior
 	s.getNow = time.Now
 	s.getNow = time.Now
@@ -520,14 +523,16 @@ func (s *Supervisor) handleFailedService(id serviceID, err interface{}, stacktra
 
 
 func (s *Supervisor) runService(service Service, id serviceID) {
 func (s *Supervisor) runService(service Service, id serviceID) {
 	go func() {
 	go func() {
-		defer func() {
-			if r := recover(); r != nil {
-				buf := make([]byte, 65535, 65535)
-				written := runtime.Stack(buf, false)
-				buf = buf[:written]
-				s.fail(id, r, buf)
-			}
-		}()
+		if s.recoverPanics {
+			defer func() {
+				if r := recover(); r != nil {
+					buf := make([]byte, 65535, 65535)
+					written := runtime.Stack(buf, false)
+					buf = buf[:written]
+					s.fail(id, r, buf)
+				}
+			}()
+		}
 
 
 		service.Serve()
 		service.Serve()
 
 
@@ -639,7 +644,10 @@ RemoveAndWait will remove the given service from the Supervisor and attempt
 to Stop() it. It will wait up to the given timeout value for the service to
 to Stop() it. It will wait up to the given timeout value for the service to
 terminate. A timeout value of 0 means to wait forever.
 terminate. A timeout value of 0 means to wait forever.
 
 
-If a nil error is returned from this function
+If a nil error is returned from this function, then the service was
+terminated normally. If either the supervisor terminates or the timeout
+passes, ErrTimeout is returned. (If this isn't even the right supervisor
+ErrWrongSupervisor is returned.)
 */
 */
 func (s *Supervisor) RemoveAndWait(id ServiceToken, timeout time.Duration) error {
 func (s *Supervisor) RemoveAndWait(id ServiceToken, timeout time.Duration) error {
 	sID := supervisorID(id.id >> 32)
 	sID := supervisorID(id.id >> 32)

+ 8 - 8
vendor/manifest

@@ -74,6 +74,14 @@
 			"branch": "master",
 			"branch": "master",
 			"notests": true
 			"notests": true
 		},
 		},
+		{
+			"importpath": "github.com/calmh/suture",
+			"repository": "https://github.com/calmh/suture",
+			"vcs": "git",
+			"revision": "2741a6bb8fdeba8f30c948c83756edc4dd21b9c6",
+			"branch": "master",
+			"notests": true
+		},
 		{
 		{
 			"importpath": "github.com/calmh/xdr",
 			"importpath": "github.com/calmh/xdr",
 			"repository": "https://github.com/calmh/xdr",
 			"repository": "https://github.com/calmh/xdr",
@@ -432,14 +440,6 @@
 			"branch": "master",
 			"branch": "master",
 			"notests": true
 			"notests": true
 		},
 		},
-		{
-			"importpath": "github.com/thejerf/suture",
-			"repository": "https://github.com/thejerf/suture",
-			"vcs": "git",
-			"revision": "3f1fb62fe0a3cc6429122d7dc45588a8b59c5bb6",
-			"branch": "master",
-			"notests": true
-		},
 		{
 		{
 			"importpath": "github.com/tjfoc/gmsm/sm4",
 			"importpath": "github.com/tjfoc/gmsm/sm4",
 			"repository": "https://github.com/tjfoc/gmsm",
 			"repository": "https://github.com/tjfoc/gmsm",