Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util/timedutil: add sync.Pools for *time.Timer and *timeutil.Timer #13466

Merged
merged 1 commit into from
Feb 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pkg/kv/dist_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -1138,8 +1138,8 @@ func (ds *DistSender) sendToReplicas(
// Wait for completions. This loop will retry operations that fail
// with errors that reflect per-replica state and may succeed on
// other replicas.
var sendNextTimer timeutil.Timer
var slowTimer timeutil.Timer
sendNextTimer := timeutil.NewTimer()
slowTimer := timeutil.NewTimer()
defer sendNextTimer.Stop()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
Expand Down
6 changes: 3 additions & 3 deletions pkg/storage/replica.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ func (r *Replica) redirectOnOrAcquireLease(ctx context.Context) (LeaseStatus, *r

// Wait for the range lease to finish, or the context to expire.
pErr = func() *roachpb.Error {
var slowTimer timeutil.Timer
slowTimer := timeutil.NewTimer()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
for {
Expand Down Expand Up @@ -1676,7 +1676,7 @@ func (r *Replica) beginCmds(ctx context.Context, ba *roachpb.BatchRequest) (*end
// However, the command queue assumes that commands don't drop
// out before their prerequisites, so we still have to wait it
// out.
var slowTimer timeutil.Timer
slowTimer := timeutil.NewTimer()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
for _, ch := range chans {
Expand Down Expand Up @@ -2158,7 +2158,7 @@ func (r *Replica) tryAddWriteCmd(
// If the command was accepted by raft, wait for the range to apply it.
ctxDone := ctx.Done()
shouldQuiesce := r.store.stopper.ShouldQuiesce()
var slowTimer timeutil.Timer
slowTimer := timeutil.NewTimer()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
for {
Expand Down
42 changes: 35 additions & 7 deletions pkg/util/timeutil/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@

package timeutil

import "time"
import (
"sync"
"time"
)

var timerPool = sync.Pool{
New: func() interface{} {
return &Timer{}
},
}
var timeTimerPool sync.Pool

// The Timer type represents a single event. When the Timer expires,
// the current time will be sent on Timer.C.
Expand Down Expand Up @@ -53,15 +63,25 @@ type Timer struct {
Read bool
}

// NewTimer allocates a new timer.
func NewTimer() *Timer {
return timerPool.Get().(*Timer)
}

// Reset changes the timer to expire after duration d and returns
// the new value of the timer. This method includes the fix proposed
// in https://github.com/golang/go/issues/11513#issuecomment-157062583,
// but requires users of Timer to set Timer.Read to true whenever
// they successfully read from the Timer's channel. Reset operates on
// and returns a value so that Timer can be stack allocated.
// they successfully read from the Timer's channel.
func (t *Timer) Reset(d time.Duration) {
if t.timer == nil {
t.timer = time.NewTimer(d)
switch timer := timeTimerPool.Get(); timer {
case nil:
t.timer = time.NewTimer(d)
default:
t.timer = timer.(*time.Timer)
t.timer.Reset(d)
}
t.C = t.timer.C
return
}
Expand All @@ -77,8 +97,16 @@ func (t *Timer) Reset(d time.Duration) {
// or had never been initialized with a call to Timer.Reset. Stop does not
// close the channel, to prevent a read from succeeding incorrectly.
func (t *Timer) Stop() bool {
if t.timer == nil {
return false
var res bool
if t.timer != nil {
res = t.timer.Stop()
if res {
// Only place the timer back in the pool if we successfully stopped
// it. Otherwise, we'd have to read from the channel if !t.Read.
timeTimerPool.Put(t.timer)
}
t.timer = nil
}
return t.timer.Stop()
timerPool.Put(t)
return res
}