github.com/uber/kraken@v0.1.4/utils/timeutil/timer.go (about)

     1  // Copyright (c) 2016-2019 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package timeutil
    15  
    16  import (
    17  	"sync"
    18  	"time"
    19  )
    20  
    21  // Timer is a thread-safe adaptation of time.Timer intended for timeouts which
    22  // may be periodically invalidated. A Timer can be started and cancelled multiple
    23  // times before the Timer fires. Once a Timer fires, it cannot be used again.
    24  type Timer struct {
    25  	// C will be closed once the Timer fires.
    26  	C chan struct{}
    27  
    28  	sync.Mutex
    29  	timer    *time.Timer
    30  	cancel   chan bool
    31  	duration time.Duration
    32  }
    33  
    34  // NewTimer creates a new Timer which is set to the given duration.
    35  func NewTimer(d time.Duration) *Timer {
    36  	return &Timer{
    37  		C:        make(chan struct{}),
    38  		cancel:   make(chan bool),
    39  		duration: d,
    40  	}
    41  }
    42  
    43  // Start starts the Timer. Returns false if the timer has already started, or
    44  // if the timer has already fired.
    45  func (t *Timer) Start() bool {
    46  	t.Lock()
    47  	defer t.Unlock()
    48  
    49  	if t.timer != nil {
    50  		// Timer has already started.
    51  		return false
    52  	}
    53  	t.timer = time.NewTimer(t.duration)
    54  
    55  	// Must copy this reference since t.timer will be nil if Cancel is called.
    56  	c := t.timer.C
    57  
    58  	go func() {
    59  		select {
    60  		case <-c:
    61  			close(t.C)
    62  		case <-t.cancel:
    63  		}
    64  	}()
    65  
    66  	return true
    67  }
    68  
    69  // Cancel cancels the Timer. Returns false if the timer has not started, or
    70  // if the timer has already fired.
    71  func (t *Timer) Cancel() bool {
    72  	t.Lock()
    73  	defer t.Unlock()
    74  
    75  	if t.timer == nil {
    76  		// Timer has not started.
    77  		return false
    78  	}
    79  	if !t.timer.Stop() {
    80  		// Timer already fired.
    81  		return false
    82  	}
    83  	// Let the goroutine created by Start exit.
    84  	t.cancel <- true
    85  	t.timer = nil
    86  	return true
    87  }