lib/util: Add caller info to service (ref #5932) (#5973)

This commit is contained in:
Simon Frei 2019-10-16 09:06:16 +02:00 committed by Jakob Borg
parent a0c9db1d09
commit 031684116b
2 changed files with 49 additions and 3 deletions

View File

@ -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)
}

View File

@ -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()
}