all: Remove global events.Default (ref #4085) (#5886)

This commit is contained in:
Simon Frei
2019-08-15 16:29:37 +02:00
committed by GitHub
parent f6f696c6c5
commit b1c74860e8
46 changed files with 467 additions and 374 deletions

View File

@@ -10,11 +10,11 @@ import (
"os"
"strings"
"github.com/syncthing/syncthing/lib/logger"
liblogger "github.com/syncthing/syncthing/lib/logger"
)
var (
dl = logger.DefaultLogger.NewFacility("events", "Event generation and logging")
dl = liblogger.DefaultLogger.NewFacility("events", "Event generation and logging")
)
func init() {

View File

@@ -13,7 +13,10 @@ import (
"runtime"
"time"
"github.com/thejerf/suture"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
)
type EventType int
@@ -51,7 +54,10 @@ const (
AllEvents = (1 << iota) - 1
)
var runningTests = false
var (
runningTests = false
errNoop = errors.New("method of a noop object called")
)
const eventLogTimeout = 15 * time.Millisecond
@@ -199,13 +205,21 @@ func UnmarshalEventType(s string) EventType {
const BufferSize = 64
type Logger struct {
subs []*Subscription
type Logger interface {
suture.Service
Log(t EventType, data interface{})
Subscribe(mask EventType) Subscription
}
type logger struct {
suture.Service
subs []*subscription
nextSubscriptionIDs []int
nextGlobalID int
timeout *time.Timer
events chan Event
funcs chan func()
toUnsubscribe chan *subscription
stop chan struct{}
}
@@ -219,19 +233,17 @@ type Event struct {
Data interface{} `json:"data"`
}
type Subscription struct {
mask EventType
events chan Event
timeout *time.Timer
type Subscription interface {
C() <-chan Event
Poll(timeout time.Duration) (Event, error)
Unsubscribe()
}
var Default = NewLogger()
func init() {
// The default logger never stops. To ensure this we nil out the stop
// channel so any attempt to stop it will panic.
Default.stop = nil
go Default.Serve()
type subscription struct {
mask EventType
events chan Event
toUnsubscribe chan *subscription
timeout *time.Timer
}
var (
@@ -239,13 +251,14 @@ var (
ErrClosed = errors.New("closed")
)
func NewLogger() *Logger {
l := &Logger{
timeout: time.NewTimer(time.Second),
events: make(chan Event, BufferSize),
funcs: make(chan func()),
stop: make(chan struct{}),
func NewLogger() Logger {
l := &logger{
timeout: time.NewTimer(time.Second),
events: make(chan Event, BufferSize),
funcs: make(chan func()),
toUnsubscribe: make(chan *subscription),
}
l.Service = util.AsService(l.serve)
// Make sure the timer is in the stopped state and hasn't fired anything
// into the channel.
if !l.timeout.Stop() {
@@ -254,7 +267,7 @@ func NewLogger() *Logger {
return l
}
func (l *Logger) Serve() {
func (l *logger) serve(stop chan struct{}) {
loop:
for {
select {
@@ -263,10 +276,13 @@ loop:
l.sendEvent(e)
case fn := <-l.funcs:
// Subscriptions etc are handled here.
// Subscriptions are handled here.
fn()
case <-l.stop:
case s := <-l.toUnsubscribe:
l.unsubscribe(s)
case <-stop:
break loop
}
}
@@ -279,11 +295,7 @@ loop:
}
}
func (l *Logger) Stop() {
close(l.stop)
}
func (l *Logger) Log(t EventType, data interface{}) {
func (l *logger) Log(t EventType, data interface{}) {
l.events <- Event{
Time: time.Now(),
Type: t,
@@ -292,7 +304,7 @@ func (l *Logger) Log(t EventType, data interface{}) {
}
}
func (l *Logger) sendEvent(e Event) {
func (l *logger) sendEvent(e Event) {
l.nextGlobalID++
dl.Debugln("log", l.nextGlobalID, e.Type, e.Data)
@@ -323,15 +335,16 @@ func (l *Logger) sendEvent(e Event) {
}
}
func (l *Logger) Subscribe(mask EventType) *Subscription {
res := make(chan *Subscription)
func (l *logger) Subscribe(mask EventType) Subscription {
res := make(chan Subscription)
l.funcs <- func() {
dl.Debugln("subscribe", mask)
s := &Subscription{
mask: mask,
events: make(chan Event, BufferSize),
timeout: time.NewTimer(0),
s := &subscription{
mask: mask,
events: make(chan Event, BufferSize),
toUnsubscribe: l.toUnsubscribe,
timeout: time.NewTimer(0),
}
// We need to create the timeout timer in the stopped, non-fired state so
@@ -355,32 +368,30 @@ func (l *Logger) Subscribe(mask EventType) *Subscription {
return <-res
}
func (l *Logger) Unsubscribe(s *Subscription) {
l.funcs <- func() {
dl.Debugln("unsubscribe")
for i, ss := range l.subs {
if s == ss {
last := len(l.subs) - 1
func (l *logger) unsubscribe(s *subscription) {
dl.Debugln("unsubscribe", s.mask)
for i, ss := range l.subs {
if s == ss {
last := len(l.subs) - 1
l.subs[i] = l.subs[last]
l.subs[last] = nil
l.subs = l.subs[:last]
l.subs[i] = l.subs[last]
l.subs[last] = nil
l.subs = l.subs[:last]
l.nextSubscriptionIDs[i] = l.nextSubscriptionIDs[last]
l.nextSubscriptionIDs[last] = 0
l.nextSubscriptionIDs = l.nextSubscriptionIDs[:last]
l.nextSubscriptionIDs[i] = l.nextSubscriptionIDs[last]
l.nextSubscriptionIDs[last] = 0
l.nextSubscriptionIDs = l.nextSubscriptionIDs[:last]
break
}
break
}
close(s.events)
}
close(s.events)
}
// Poll returns an event from the subscription or an error if the poll times
// out of the event channel is closed. Poll should not be called concurrently
// from multiple goroutines for a single subscription.
func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
func (s *subscription) Poll(timeout time.Duration) (Event, error) {
dl.Debugln("poll", timeout)
s.timeout.Reset(timeout)
@@ -409,12 +420,16 @@ func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
}
}
func (s *Subscription) C() <-chan Event {
func (s *subscription) C() <-chan Event {
return s.events
}
func (s *subscription) Unsubscribe() {
s.toUnsubscribe <- s
}
type bufferedSubscription struct {
sub *Subscription
sub Subscription
buf []Event
next int
cur int // Current SubscriptionID
@@ -426,7 +441,7 @@ type BufferedSubscription interface {
Since(id int, into []Event, timeout time.Duration) []Event
}
func NewBufferedSubscription(s *Subscription, size int) BufferedSubscription {
func NewBufferedSubscription(s Subscription, size int) BufferedSubscription {
bs := &bufferedSubscription{
sub: s,
buf: make([]Event, size),
@@ -489,3 +504,29 @@ func Error(err error) *string {
str := err.Error()
return &str
}
type noopLogger struct{}
var NoopLogger Logger = &noopLogger{}
func (*noopLogger) Serve() {}
func (*noopLogger) Stop() {}
func (*noopLogger) Log(t EventType, data interface{}) {}
func (*noopLogger) Subscribe(mask EventType) Subscription {
return &noopSubscription{}
}
type noopSubscription struct{}
func (*noopSubscription) C() <-chan Event {
return nil
}
func (*noopSubscription) Poll(timeout time.Duration) (Event, error) {
return Event{}, errNoop
}
func (*noopSubscription) Unsubscribe() {}

View File

@@ -33,7 +33,7 @@ func TestSubscriber(t *testing.T) {
go l.Serve()
s := l.Subscribe(0)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
if s == nil {
t.Fatal("Unexpected nil Subscription")
}
@@ -45,7 +45,7 @@ func TestTimeout(t *testing.T) {
go l.Serve()
s := l.Subscribe(0)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
_, err := s.Poll(timeout)
if err != ErrTimeout {
t.Fatal("Unexpected non-Timeout error:", err)
@@ -59,7 +59,7 @@ func TestEventBeforeSubscribe(t *testing.T) {
l.Log(DeviceConnected, "foo")
s := l.Subscribe(0)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
_, err := s.Poll(timeout)
if err != ErrTimeout {
@@ -73,7 +73,7 @@ func TestEventAfterSubscribe(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceConnected, "foo")
ev, err := s.Poll(timeout)
@@ -100,7 +100,7 @@ func TestEventAfterSubscribeIgnoreMask(t *testing.T) {
go l.Serve()
s := l.Subscribe(DeviceDisconnected)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceConnected, "foo")
_, err := s.Poll(timeout)
@@ -115,7 +115,7 @@ func TestBufferOverflow(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
// The first BufferSize events will be logged pretty much
// instantaneously. The next BufferSize events will each block for up to
@@ -147,7 +147,7 @@ func TestUnsubscribe(t *testing.T) {
t.Fatal("Unexpected error:", err)
}
l.Unsubscribe(s)
s.Unsubscribe()
l.Log(DeviceConnected, "foo")
_, err = s.Poll(timeout)
@@ -162,7 +162,7 @@ func TestGlobalIDs(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceConnected, "foo")
l.Subscribe(AllEvents)
l.Log(DeviceConnected, "bar")
@@ -194,7 +194,7 @@ func TestSubscriptionIDs(t *testing.T) {
go l.Serve()
s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
l.Log(DeviceDisconnected, "a")
l.Log(DeviceConnected, "b")
@@ -236,7 +236,7 @@ func TestBufferedSub(t *testing.T) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
bs := NewBufferedSubscription(s, 10*BufferSize)
go func() {
@@ -267,7 +267,7 @@ func BenchmarkBufferedSub(b *testing.B) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
bufferSize := BufferSize
bs := NewBufferedSubscription(s, bufferSize)
@@ -323,7 +323,7 @@ func TestSinceUsesSubscriptionId(t *testing.T) {
go l.Serve()
s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
bs := NewBufferedSubscription(s, 10*BufferSize)
l.Log(DeviceConnected, "a") // SubscriptionID = 1
@@ -390,7 +390,7 @@ func TestUnsubscribeContention(t *testing.T) {
defer listenerWg.Done()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
for {
select {
@@ -449,7 +449,7 @@ func BenchmarkLogEvent(b *testing.B) {
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
defer s.Unsubscribe()
NewBufferedSubscription(s, 1) // runs in the background
for i := 0; i < b.N; i++ {