mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-08 22:31:04 +00:00
parent
a0c9db1d09
commit
031684116b
@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -178,7 +179,7 @@ func Address(network, host string) string {
|
||||
// AsService wraps the given function to implement suture.Service by calling
|
||||
// that function on serve and closing the passed channel when Stop is called.
|
||||
func AsService(fn func(stop chan struct{})) suture.Service {
|
||||
return AsServiceWithError(func(stop chan struct{}) error {
|
||||
return asServiceWithError(func(stop chan struct{}) error {
|
||||
fn(stop)
|
||||
return nil
|
||||
})
|
||||
@ -186,6 +187,7 @@ func AsService(fn func(stop chan struct{})) suture.Service {
|
||||
|
||||
type ServiceWithError interface {
|
||||
suture.Service
|
||||
fmt.Stringer
|
||||
Error() error
|
||||
SetError(error)
|
||||
}
|
||||
@ -193,7 +195,21 @@ type ServiceWithError interface {
|
||||
// AsServiceWithError does the same as AsService, except that it keeps track
|
||||
// of an error returned by the given function.
|
||||
func AsServiceWithError(fn func(stop chan struct{}) error) ServiceWithError {
|
||||
return asServiceWithError(fn)
|
||||
}
|
||||
|
||||
// caller retrieves information about the creator of the service, i.e. the stack
|
||||
// two levels up from itself.
|
||||
func caller() string {
|
||||
pc := make([]uintptr, 1)
|
||||
_ = runtime.Callers(4, pc)
|
||||
f, _ := runtime.CallersFrames(pc).Next()
|
||||
return f.Function
|
||||
}
|
||||
|
||||
func asServiceWithError(fn func(stop chan struct{}) error) ServiceWithError {
|
||||
s := &service{
|
||||
caller: caller(),
|
||||
serve: fn,
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
@ -204,6 +220,7 @@ func AsServiceWithError(fn func(stop chan struct{}) error) ServiceWithError {
|
||||
}
|
||||
|
||||
type service struct {
|
||||
caller string
|
||||
serve func(stop chan struct{}) error
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
@ -235,7 +252,12 @@ func (s *service) Serve() {
|
||||
|
||||
func (s *service) Stop() {
|
||||
s.mut.Lock()
|
||||
close(s.stop)
|
||||
select {
|
||||
case <-s.stop:
|
||||
panic(fmt.Sprintf("Stop called more than once on %v", s))
|
||||
default:
|
||||
close(s.stop)
|
||||
}
|
||||
s.mut.Unlock()
|
||||
<-s.stopped
|
||||
}
|
||||
@ -251,3 +273,7 @@ func (s *service) SetError(err error) {
|
||||
s.err = err
|
||||
s.mut.Unlock()
|
||||
}
|
||||
|
||||
func (s *service) String() string {
|
||||
return fmt.Sprintf("Service@%p created by %v", s, s.caller)
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Defaulter struct {
|
||||
Value string
|
||||
@ -222,3 +225,20 @@ func TestCopyMatching(t *testing.T) {
|
||||
t.Error("NoCopy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilStopTwicePanic(t *testing.T) {
|
||||
s := AsService(func(stop chan struct{}) {
|
||||
<-stop
|
||||
})
|
||||
|
||||
go s.Serve()
|
||||
s.Stop()
|
||||
|
||||
defer func() {
|
||||
expected := "lib/util.TestUtilStopTwicePanic"
|
||||
if r := recover(); r == nil || !strings.Contains(r.(string), expected) {
|
||||
t.Fatalf(`expected panic containing "%v", got "%v"`, expected, r)
|
||||
}
|
||||
}()
|
||||
s.Stop()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user