common/mclock: use ping channel instead of GoSched

This commit is contained in:
Zsolt Felfoldi 2018-07-04 22:35:00 +02:00
parent 901f68d0c5
commit 7aa34bc11b
3 changed files with 31 additions and 6 deletions

View file

@ -33,15 +33,24 @@ func Now() AbsTime {
// Clock interface makes it possible to replace the monotonic system clock with // Clock interface makes it possible to replace the monotonic system clock with
// a simulated clock // a simulated clock
//
// Note: event loops capable of running with a simulated clock should listen to PingChannel.
// MonotonicClock also implements this function to ensure interface compatibility.
type Clock interface { type Clock interface {
Now() AbsTime Now() AbsTime
Sleep(time.Duration) Sleep(time.Duration)
After(time.Duration) <-chan time.Time After(time.Duration) <-chan time.Time
PingChannel() chan struct{}
} }
// MonotonicClock implements Clock using the system clock // MonotonicClock implements Clock using the system clock
type MonotonicClock struct{} type MonotonicClock struct{}
// PingChannel implements Clock by returning a dummy nil channel
func (MonotonicClock) PingChannel() chan struct{} {
return nil
}
// Now implements Clock // Now implements Clock
func (MonotonicClock) Now() AbsTime { func (MonotonicClock) Now() AbsTime {
return AbsTime(monotime.Now()) return AbsTime(monotime.Now())

View file

@ -18,7 +18,6 @@
package mclock package mclock
import ( import (
"runtime"
"sync" "sync"
"time" "time"
) )
@ -39,18 +38,23 @@ type SimulatedClock struct {
now AbsTime now AbsTime
scheduled []event scheduled []event
stop bool stop bool
pingCh chan struct{}
lock sync.RWMutex lock sync.RWMutex
} }
// NewSimulatedClock creates a new simulated clock // NewSimulatedClock creates a new simulated clock
func NewSimulatedClock(maxStep time.Duration, goSchedCount int) *SimulatedClock { func NewSimulatedClock(maxStep time.Duration, pingCount int) *SimulatedClock {
s := &SimulatedClock{scheduled: make([]event, 0, 100)} s := &SimulatedClock{scheduled: make([]event, 0, 100), pingCh: make(chan struct{})}
go func() { go func() {
lastScheduled := 0 lastScheduled := 0
for { for {
for i := 0; i < goSchedCount; i++ { timeout := time.After(maxStep / 100)
runtime.Gosched() for i := 0; i < pingCount; i++ {
select {
case s.pingCh <- struct{}{}:
case <-timeout:
}
} }
s.lock.Lock() s.lock.Lock()
if s.stop { if s.stop {
@ -76,6 +80,11 @@ func NewSimulatedClock(maxStep time.Duration, goSchedCount int) *SimulatedClock
return s return s
} }
// PingChannel returns a channel that event loops should listen to
func (s *SimulatedClock) PingChannel() chan struct{} {
return s.pingCh
}
// Stop stops the clock (Sleeps and Afters will never return after this) // Stop stops the clock (Sleeps and Afters will never return after this)
func (s *SimulatedClock) Stop() { func (s *SimulatedClock) Stop() {
s.lock.Lock() s.lock.Lock()
@ -97,7 +106,13 @@ func (s *SimulatedClock) Sleep(d time.Duration) {
s.insert(d, func() { s.insert(d, func() {
close(done) close(done)
}) })
<-done for {
select {
case <-done:
return
case <-s.pingCh:
}
}
} }
// After implements Clock // After implements Clock

View file

@ -86,6 +86,7 @@ func testFreeClientPool(t *testing.T, connLimit, clientCount int) {
loop: loop:
for { for {
select { select {
case <-clock.PingChannel():
case <-tickCh: case <-tickCh:
i := rand.Intn(clientCount) i := rand.Intn(clientCount)
if connected[i] { if connected[i] {