Chromium Code Reviews| Index: common/clock/systemtimer.go |
| diff --git a/common/clock/systemtimer.go b/common/clock/systemtimer.go |
| index 0389a2ebf664b91e4790f925c3fe9e5f44c27664..926154b0aab9afd28b696042f403b1b981f4a4fe 100644 |
| --- a/common/clock/systemtimer.go |
| +++ b/common/clock/systemtimer.go |
| @@ -6,32 +6,89 @@ package clock |
| import ( |
| "time" |
| + |
| + "golang.org/x/net/context" |
| ) |
| type systemTimer struct { |
| - T *time.Timer // The underlying timer. Starts as nil, is initialized on Reset. |
| + // base is the underlying timer. It starts as nil, and is initialized on |
| + // Reset. |
| + ctx context.Context |
|
iannucci
2016/02/10 22:30:28
base? ctx?
dnj (Google)
2016/02/11 01:26:54
Done.
|
| + |
| + // timerC is the current timer channel. |
| + timerC chan TimerResult |
| + // timerDoneC is a signal channel to alert our monitor that this timer has |
| + // been canceled. |
| + timerStoppedC chan struct{} |
|
iannucci
2016/02/10 22:30:28
comments
dnj (Google)
2016/02/11 01:26:54
Done.
|
| + // timerMonitorResultC returns true if the timer monitor was prematurely |
| + // terminated, false if not. |
| + timerMonitorResultC chan bool |
| } |
| var _ Timer = (*systemTimer)(nil) |
| -func (t *systemTimer) GetC() <-chan time.Time { |
| - if t.T == nil { |
| - return nil |
| - } |
| - return t.T.C |
| +func (t *systemTimer) GetC() <-chan TimerResult { |
| + return t.timerC |
| } |
| -func (t *systemTimer) Reset(d time.Duration) bool { |
| - if t.T == nil { |
| - t.T = time.NewTimer(d) |
| - return false |
| +func (t *systemTimer) Reset(d time.Duration) (running bool) { |
| + running = t.Stop() |
| + t.reset() |
| + |
| + // If our Context is already done, finish immediately. |
| + select { |
|
iannucci
2016/02/10 22:30:28
I'd use t.ctx.Err() instead
dnj (Google)
2016/02/11 01:26:54
Done.
|
| + case <-t.ctx.Done(): |
| + t.timerC <- TimerResult{Time: time.Now(), Err: t.ctx.Err()} |
| + return |
| + default: |
| + break |
| } |
| - return t.T.Reset(d) |
| + |
| + // Start a monitor goroutine and our actual timer. Copy our channels, since |
| + // future stop/reset will change the systemTimer's values and our goroutine |
| + // should only operate on this round's values. |
| + timerC := t.timerC |
| + timerStoppedC := make(chan struct{}) |
| + timerMonitorResultC := make(chan bool, 1) |
| + go func() { |
| + interrupted := false |
| + defer func() { |
| + timerMonitorResultC <- interrupted |
| + }() |
| + |
| + select { |
| + case <-timerStoppedC: |
| + interrupted = true |
| + |
| + case <-t.ctx.Done(): |
| + timerC <- TimerResult{Time: time.Now(), Err: t.ctx.Err()} |
| + case now := <-time.After(d): |
| + // For determinism, prioritize Context cancellation over timer expiration. |
| + select { |
| + case <-t.ctx.Done(): |
| + timerC <- TimerResult{Time: now, Err: t.ctx.Err()} |
| + default: |
| + break |
| + } |
|
iannucci
2016/02/10 22:30:28
also Err()
dnj (Google)
2016/02/11 01:26:54
Done.
|
| + |
| + timerC <- TimerResult{Time: now} |
| + } |
| + }() |
| + |
| + t.timerStoppedC = timerStoppedC |
| + t.timerMonitorResultC = timerMonitorResultC |
| + return |
| } |
| func (t *systemTimer) Stop() bool { |
| - if t.T == nil { |
| + if t.timerStoppedC == nil { |
| return false |
| } |
| - return t.T.Stop() |
| + close(t.timerStoppedC) |
| + t.timerStoppedC = nil |
| + return <-t.timerMonitorResultC |
| +} |
| + |
| +func (t *systemTimer) reset() { |
|
iannucci
2016/02/10 22:30:28
let's inline this
dnj (Google)
2016/02/11 01:26:54
Done.
|
| + t.timerC = make(chan TimerResult, 1) |
| } |