mirror of
https://github.com/minio/minio.git
synced 2025-02-11 05:38:07 -05:00
- over the course of a project history every maintainer needs to update its dependency packages, the problem essentially with godep is manipulating GOPATH - this manipulation leads to static objects created at different locations which end up conflicting with the overall functionality of golang. This also leads to broken builds. There is no easier way out of this other than asking developers to do 'godep restore' all the time. Which perhaps as a practice doesn't sound like a clean solution. On the other hand 'godep restore' has its own set of problems. - govendor is a right tool but a stop gap tool until we wait for golangs official 1.5 version which fixes this vendoring issue once and for all. - govendor provides consistency in terms of how import paths should be handled unlike manipulation GOPATH. This has advantages - no more compiled objects being referenced in GOPATH and build time GOPATH manging which leads to conflicts. - proper import paths referencing the exact package a project is dependent on. govendor is simple and provides the minimal necessary tooling to achieve this. For now this is the right solution.
364 lines
8.6 KiB
Go
364 lines
8.6 KiB
Go
package clock
|
|
|
|
import (
|
|
"runtime"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Clock represents an interface to the functions in the standard library time
|
|
// package. Two implementations are available in the clock package. The first
|
|
// is a real-time clock which simply wraps the time package's functions. The
|
|
// second is a mock clock which will only make forward progress when
|
|
// programmatically adjusted.
|
|
type Clock interface {
|
|
After(d time.Duration) <-chan time.Time
|
|
AfterFunc(d time.Duration, f func()) *Timer
|
|
Now() time.Time
|
|
Sleep(d time.Duration)
|
|
Tick(d time.Duration) <-chan time.Time
|
|
Ticker(d time.Duration) *Ticker
|
|
Timer(d time.Duration) *Timer
|
|
}
|
|
|
|
// New returns an instance of a real-time clock.
|
|
func New() Clock {
|
|
return &clock{}
|
|
}
|
|
|
|
// clock implements a real-time clock by simply wrapping the time package functions.
|
|
type clock struct{}
|
|
|
|
func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) }
|
|
|
|
func (c *clock) AfterFunc(d time.Duration, f func()) *Timer {
|
|
return &Timer{timer: time.AfterFunc(d, f)}
|
|
}
|
|
|
|
func (c *clock) Now() time.Time { return time.Now() }
|
|
|
|
func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }
|
|
|
|
func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) }
|
|
|
|
func (c *clock) Ticker(d time.Duration) *Ticker {
|
|
t := time.NewTicker(d)
|
|
return &Ticker{C: t.C, ticker: t}
|
|
}
|
|
|
|
func (c *clock) Timer(d time.Duration) *Timer {
|
|
t := time.NewTimer(d)
|
|
return &Timer{C: t.C, timer: t}
|
|
}
|
|
|
|
// Mock represents a mock clock that only moves forward programmically.
|
|
// It can be preferable to a real-time clock when testing time-based functionality.
|
|
type Mock struct {
|
|
mu sync.Mutex
|
|
now time.Time // current time
|
|
timers clockTimers // tickers & timers
|
|
|
|
calls Calls
|
|
waiting []waiting
|
|
callsMutex sync.Mutex
|
|
}
|
|
|
|
// NewMock returns an instance of a mock clock.
|
|
// The current time of the mock clock on initialization is the Unix epoch.
|
|
func NewMock() *Mock {
|
|
return &Mock{now: time.Unix(0, 0)}
|
|
}
|
|
|
|
// Add moves the current time of the mock clock forward by the duration.
|
|
// This should only be called from a single goroutine at a time.
|
|
func (m *Mock) Add(d time.Duration) {
|
|
// Calculate the final current time.
|
|
t := m.now.Add(d)
|
|
|
|
// Continue to execute timers until there are no more before the new time.
|
|
for {
|
|
if !m.runNextTimer(t) {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Ensure that we end with the new time.
|
|
m.mu.Lock()
|
|
m.now = t
|
|
m.mu.Unlock()
|
|
|
|
// Give a small buffer to make sure the other goroutines get handled.
|
|
gosched()
|
|
}
|
|
|
|
// runNextTimer executes the next timer in chronological order and moves the
|
|
// current time to the timer's next tick time. The next time is not executed if
|
|
// it's next time if after the max time. Returns true if a timer is executed.
|
|
func (m *Mock) runNextTimer(max time.Time) bool {
|
|
m.mu.Lock()
|
|
|
|
// Sort timers by time.
|
|
sort.Sort(m.timers)
|
|
|
|
// If we have no more timers then exit.
|
|
if len(m.timers) == 0 {
|
|
m.mu.Unlock()
|
|
return false
|
|
}
|
|
|
|
// Retrieve next timer. Exit if next tick is after new time.
|
|
t := m.timers[0]
|
|
if t.Next().After(max) {
|
|
m.mu.Unlock()
|
|
return false
|
|
}
|
|
|
|
// Move "now" forward and unlock clock.
|
|
m.now = t.Next()
|
|
m.mu.Unlock()
|
|
|
|
// Execute timer.
|
|
t.Tick(m.now)
|
|
return true
|
|
}
|
|
|
|
// After waits for the duration to elapse and then sends the current time on the returned channel.
|
|
func (m *Mock) After(d time.Duration) <-chan time.Time {
|
|
defer m.inc(&m.calls.After)
|
|
return m.Timer(d).C
|
|
}
|
|
|
|
// AfterFunc waits for the duration to elapse and then executes a function.
|
|
// A Timer is returned that can be stopped.
|
|
func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
|
|
defer m.inc(&m.calls.AfterFunc)
|
|
t := m.Timer(d)
|
|
t.C = nil
|
|
t.fn = f
|
|
return t
|
|
}
|
|
|
|
// Now returns the current wall time on the mock clock.
|
|
func (m *Mock) Now() time.Time {
|
|
defer m.inc(&m.calls.Now)
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
return m.now
|
|
}
|
|
|
|
// Sleep pauses the goroutine for the given duration on the mock clock.
|
|
// The clock must be moved forward in a separate goroutine.
|
|
func (m *Mock) Sleep(d time.Duration) {
|
|
defer m.inc(&m.calls.Sleep)
|
|
<-m.After(d)
|
|
}
|
|
|
|
// Tick is a convenience function for Ticker().
|
|
// It will return a ticker channel that cannot be stopped.
|
|
func (m *Mock) Tick(d time.Duration) <-chan time.Time {
|
|
defer m.inc(&m.calls.Tick)
|
|
return m.Ticker(d).C
|
|
}
|
|
|
|
// Ticker creates a new instance of Ticker.
|
|
func (m *Mock) Ticker(d time.Duration) *Ticker {
|
|
defer m.inc(&m.calls.Ticker)
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
ch := make(chan time.Time)
|
|
t := &Ticker{
|
|
C: ch,
|
|
c: ch,
|
|
mock: m,
|
|
d: d,
|
|
next: m.now.Add(d),
|
|
}
|
|
m.timers = append(m.timers, (*internalTicker)(t))
|
|
return t
|
|
}
|
|
|
|
// Timer creates a new instance of Timer.
|
|
func (m *Mock) Timer(d time.Duration) *Timer {
|
|
defer m.inc(&m.calls.Timer)
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
ch := make(chan time.Time)
|
|
t := &Timer{
|
|
C: ch,
|
|
c: ch,
|
|
mock: m,
|
|
next: m.now.Add(d),
|
|
}
|
|
m.timers = append(m.timers, (*internalTimer)(t))
|
|
return t
|
|
}
|
|
|
|
func (m *Mock) removeClockTimer(t clockTimer) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
for i, timer := range m.timers {
|
|
if timer == t {
|
|
copy(m.timers[i:], m.timers[i+1:])
|
|
m.timers[len(m.timers)-1] = nil
|
|
m.timers = m.timers[:len(m.timers)-1]
|
|
break
|
|
}
|
|
}
|
|
sort.Sort(m.timers)
|
|
}
|
|
|
|
func (m *Mock) inc(addr *uint32) {
|
|
m.callsMutex.Lock()
|
|
defer m.callsMutex.Unlock()
|
|
*addr++
|
|
var newWaiting []waiting
|
|
for _, w := range m.waiting {
|
|
if m.calls.atLeast(w.expected) {
|
|
close(w.done)
|
|
continue
|
|
}
|
|
newWaiting = append(newWaiting, w)
|
|
}
|
|
m.waiting = newWaiting
|
|
}
|
|
|
|
// Wait waits for at least the relevant calls before returning. The expected
|
|
// Calls are always over the lifetime of the Mock. Values in the Calls struct
|
|
// are used as the minimum number of calls, this allows you to wait for only
|
|
// the calls you care about.
|
|
func (m *Mock) Wait(s Calls) {
|
|
m.callsMutex.Lock()
|
|
if m.calls.atLeast(s) {
|
|
m.callsMutex.Unlock()
|
|
return
|
|
}
|
|
done := make(chan struct{})
|
|
m.waiting = append(m.waiting, waiting{expected: s, done: done})
|
|
m.callsMutex.Unlock()
|
|
<-done
|
|
}
|
|
|
|
// clockTimer represents an object with an associated start time.
|
|
type clockTimer interface {
|
|
Next() time.Time
|
|
Tick(time.Time)
|
|
}
|
|
|
|
// clockTimers represents a list of sortable timers.
|
|
type clockTimers []clockTimer
|
|
|
|
func (a clockTimers) Len() int { return len(a) }
|
|
func (a clockTimers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) }
|
|
|
|
// Timer represents a single event.
|
|
// The current time will be sent on C, unless the timer was created by AfterFunc.
|
|
type Timer struct {
|
|
C <-chan time.Time
|
|
c chan time.Time
|
|
timer *time.Timer // realtime impl, if set
|
|
next time.Time // next tick time
|
|
mock *Mock // mock clock, if set
|
|
fn func() // AfterFunc function, if set
|
|
}
|
|
|
|
// Stop turns off the ticker.
|
|
func (t *Timer) Stop() {
|
|
if t.timer != nil {
|
|
t.timer.Stop()
|
|
} else {
|
|
t.mock.removeClockTimer((*internalTimer)(t))
|
|
}
|
|
}
|
|
|
|
type internalTimer Timer
|
|
|
|
func (t *internalTimer) Next() time.Time { return t.next }
|
|
func (t *internalTimer) Tick(now time.Time) {
|
|
if t.fn != nil {
|
|
t.fn()
|
|
} else {
|
|
t.c <- now
|
|
}
|
|
t.mock.removeClockTimer((*internalTimer)(t))
|
|
gosched()
|
|
}
|
|
|
|
// Ticker holds a channel that receives "ticks" at regular intervals.
|
|
type Ticker struct {
|
|
C <-chan time.Time
|
|
c chan time.Time
|
|
ticker *time.Ticker // realtime impl, if set
|
|
next time.Time // next tick time
|
|
mock *Mock // mock clock, if set
|
|
d time.Duration // time between ticks
|
|
}
|
|
|
|
// Stop turns off the ticker.
|
|
func (t *Ticker) Stop() {
|
|
if t.ticker != nil {
|
|
t.ticker.Stop()
|
|
} else {
|
|
t.mock.removeClockTimer((*internalTicker)(t))
|
|
}
|
|
}
|
|
|
|
type internalTicker Ticker
|
|
|
|
func (t *internalTicker) Next() time.Time { return t.next }
|
|
func (t *internalTicker) Tick(now time.Time) {
|
|
select {
|
|
case t.c <- now:
|
|
case <-time.After(1 * time.Millisecond):
|
|
}
|
|
t.next = now.Add(t.d)
|
|
gosched()
|
|
}
|
|
|
|
// Sleep momentarily so that other goroutines can process.
|
|
func gosched() { runtime.Gosched() }
|
|
|
|
// Calls keeps track of the count of calls for each of the methods on the Clock
|
|
// interface.
|
|
type Calls struct {
|
|
After uint32
|
|
AfterFunc uint32
|
|
Now uint32
|
|
Sleep uint32
|
|
Tick uint32
|
|
Ticker uint32
|
|
Timer uint32
|
|
}
|
|
|
|
// atLeast returns true if at least the number of calls in o have been made.
|
|
func (c Calls) atLeast(o Calls) bool {
|
|
if c.After < o.After {
|
|
return false
|
|
}
|
|
if c.AfterFunc < o.AfterFunc {
|
|
return false
|
|
}
|
|
if c.Now < o.Now {
|
|
return false
|
|
}
|
|
if c.Sleep < o.Sleep {
|
|
return false
|
|
}
|
|
if c.Tick < o.Tick {
|
|
return false
|
|
}
|
|
if c.Ticker < o.Ticker {
|
|
return false
|
|
}
|
|
if c.Timer < o.Timer {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type waiting struct {
|
|
expected Calls
|
|
done chan struct{}
|
|
}
|