From 5d464364a8c5408c3f117b125e8d709fc0ceb315 Mon Sep 17 00:00:00 2001 From: caffix Date: Fri, 8 Dec 2017 15:51:18 -0500 Subject: [PATCH] fixed the racy test and removed all the calls to Sleep --- p2p/trust/metric.go | 90 ++++++++++++++++++++++++++++++++++++++++ p2p/trust/metric_test.go | 26 ++++++------ 2 files changed, 104 insertions(+), 12 deletions(-) diff --git a/p2p/trust/metric.go b/p2p/trust/metric.go index beb462b20..0ee39a798 100644 --- a/p2p/trust/metric.go +++ b/p2p/trust/metric.go @@ -73,8 +73,17 @@ type TrustMetric struct { // While true, history data is not modified paused bool + // Set to true once the metric has been stopped + stopped bool + // Signal channel for stopping the trust metric go-routine stop chan struct{} + + // Slice of signal channels fired when the metric is stopped + waitForStop []chan struct{} + + // Slice of signal channels fired when the metric time interval ticker is fired + waitForTimeInterval []chan struct{} } // NewMetric returns a trust metric with the default configuration @@ -259,6 +268,83 @@ func (tm *TrustMetric) Copy() *TrustMetric { } } +// WaitForTimeIntervalToPass blocks until the metric +// go-routine ticker fire again +func (tm *TrustMetric) WaitForTimeIntervalToPass() { + interval := make(chan struct{}) + + if tm.AddTimeIntervalWaitChannel(interval) { + <-interval + } +} + +// SignalTimeIntervalPassed fires all the maintained signal channels +// and clears the group of signal channels +func (tm *TrustMetric) SignalTimeIntervalPassed() { + tm.mtx.Lock() + defer tm.mtx.Unlock() + + for _, interval := range tm.waitForTimeInterval { + interval <- struct{}{} + } + + tm.waitForTimeInterval = []chan struct{}{} +} + +// AddTimeIntervalWaitChannel adds a signal channel to a group of +// waiters for this metric. This method returns true if the channel +// was added to the group before the metric was stopped +func (tm *TrustMetric) AddTimeIntervalWaitChannel(interval chan struct{}) bool { + tm.mtx.Lock() + defer tm.mtx.Unlock() + + var added bool + + if !tm.stopped { + tm.waitForTimeInterval = append(tm.waitForTimeInterval, interval) + added = true + } + return added +} + +// WaitForStop blocks until the metric has completely stopped +func (tm *TrustMetric) WaitForStop() { + stop := make(chan struct{}) + + if tm.AddStopWaitChannel(stop) { + <-stop + } +} + +// SignalStopped fires all the maintained signal channels and sets +// metric stopped status to true +func (tm *TrustMetric) SignalStopped() { + tm.mtx.Lock() + defer tm.mtx.Unlock() + + for _, stop := range tm.waitForStop { + stop <- struct{}{} + } + + tm.stopped = true +} + +// AddStopWaitChannel adds a signal channel to a group of waiters +// for this metric. This method returns true if the channel was +// added to the group before the metric was stopped +func (tm *TrustMetric) AddStopWaitChannel(stop chan struct{}) bool { + tm.mtx.Lock() + defer tm.mtx.Unlock() + + var added bool + + if !tm.stopped { + tm.waitForStop = append(tm.waitForStop, stop) + added = true + } + return added +} + /* Private methods */ // This method is for a goroutine that handles all requests on the metric @@ -270,11 +356,15 @@ loop: select { case <-t.C: tm.NextTimeInterval() + tm.SignalTimeIntervalPassed() case <-tm.stop: // Stop all further tracking for this metric break loop } } + + // Change the status to stopped and signal the waiters + tm.SignalStopped() } // Wakes the trust metric up if it is currently paused diff --git a/p2p/trust/metric_test.go b/p2p/trust/metric_test.go index 92272615c..89077e1fa 100644 --- a/p2p/trust/metric_test.go +++ b/p2p/trust/metric_test.go @@ -61,30 +61,32 @@ func TestTrustMetricStopPause(t *testing.T) { tm := NewMetricWithConfig(config) // Allow some time intervals to pass and pause - time.Sleep(50 * time.Millisecond) + tm.NextTimeInterval() + tm.NextTimeInterval() tm.Pause() - // Give the pause some time to take place - time.Sleep(10 * time.Millisecond) first := tm.Copy().numIntervals // Allow more time to pass and check the intervals are unchanged - time.Sleep(50 * time.Millisecond) - assert.Equal(t, first, tm.numIntervals) + tm.WaitForTimeIntervalToPass() + tm.WaitForTimeIntervalToPass() + assert.Equal(t, first, tm.Copy().numIntervals) // Get the trust metric activated again tm.GoodEvents(5) // Allow some time intervals to pass and stop - time.Sleep(50 * time.Millisecond) + tm.NextTimeInterval() + tm.NextTimeInterval() tm.Stop() - // Give the stop some time to take place - time.Sleep(10 * time.Millisecond) + // Wait for the stop to take place + tm.WaitForStop() second := tm.Copy().numIntervals - // Allow more time to pass and check the intervals are unchanged - time.Sleep(50 * time.Millisecond) - assert.Equal(t, second, tm.numIntervals) + // Allow more intervals to pass and check that the number of intervals match + tm.NextTimeInterval() + tm.NextTimeInterval() + assert.Equal(t, second+2, tm.Copy().numIntervals) - if first >= second { + if first > second { t.Fatalf("numIntervals should always increase or stay the same over time") } }