github.com/XiaoMi/Gaea@v1.2.5/util/timer/timer.go (about)

     1  /*
     2  Copyright 2017 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package timer provides various enhanced timer functions.
    18  package timer
    19  
    20  import (
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/XiaoMi/Gaea/util/sync2"
    25  )
    26  
    27  // Out-of-band messages
    28  type typeAction int
    29  
    30  const (
    31  	timerStop typeAction = iota
    32  	timerReset
    33  	timerTrigger
    34  )
    35  
    36  /*
    37  Timer provides timer functionality that can be controlled
    38  by the user. You start the timer by providing it a callback function,
    39  which it will call at the specified interval.
    40  
    41  	var t = timer.NewTimer(1e9)
    42  	t.Start(KeepHouse)
    43  
    44  	func KeepHouse() {
    45  		// do house keeping work
    46  	}
    47  
    48  You can stop the timer by calling t.Stop, which is guaranteed to
    49  wait if KeepHouse is being executed.
    50  
    51  You can create an untimely trigger by calling t.Trigger. You can also
    52  schedule an untimely trigger by calling t.TriggerAfter.
    53  
    54  The timer interval can be changed on the fly by calling t.SetInterval.
    55  A zero value interval will cause the timer to wait indefinitely, and it
    56  will react only to an explicit Trigger or Stop.
    57  */
    58  type Timer struct {
    59  	interval sync2.AtomicDuration
    60  
    61  	// state management
    62  	mu      sync.Mutex
    63  	running bool
    64  
    65  	// msg is used for out-of-band messages
    66  	msg chan typeAction
    67  }
    68  
    69  // NewTimer creates a new Timer object
    70  func NewTimer(interval time.Duration) *Timer {
    71  	tm := &Timer{
    72  		msg: make(chan typeAction),
    73  	}
    74  	tm.interval.Set(interval)
    75  	return tm
    76  }
    77  
    78  // Start starts the timer.
    79  func (tm *Timer) Start(keephouse func()) {
    80  	tm.mu.Lock()
    81  	defer tm.mu.Unlock()
    82  	if tm.running {
    83  		return
    84  	}
    85  	tm.running = true
    86  	go tm.run(keephouse)
    87  }
    88  
    89  func (tm *Timer) run(keephouse func()) {
    90  	for {
    91  		var ch <-chan time.Time
    92  		interval := tm.interval.Get()
    93  		if interval <= 0 {
    94  			ch = nil
    95  		} else {
    96  			ch = time.After(interval)
    97  		}
    98  		select {
    99  		case action := <-tm.msg:
   100  			switch action {
   101  			case timerStop:
   102  				return
   103  			case timerReset:
   104  				continue
   105  			}
   106  		case <-ch:
   107  		}
   108  		keephouse()
   109  	}
   110  }
   111  
   112  // SetInterval changes the wait interval.
   113  // It will cause the timer to restart the wait.
   114  func (tm *Timer) SetInterval(ns time.Duration) {
   115  	tm.interval.Set(ns)
   116  	tm.mu.Lock()
   117  	defer tm.mu.Unlock()
   118  	if tm.running {
   119  		tm.msg <- timerReset
   120  	}
   121  }
   122  
   123  // Trigger will cause the timer to immediately execute the keephouse function.
   124  // It will then cause the timer to restart the wait.
   125  func (tm *Timer) Trigger() {
   126  	tm.mu.Lock()
   127  	defer tm.mu.Unlock()
   128  	if tm.running {
   129  		tm.msg <- timerTrigger
   130  	}
   131  }
   132  
   133  // TriggerAfter waits for the specified duration and triggers the next event.
   134  func (tm *Timer) TriggerAfter(duration time.Duration) {
   135  	go func() {
   136  		time.Sleep(duration)
   137  		tm.Trigger()
   138  	}()
   139  }
   140  
   141  // Stop will stop the timer. It guarantees that the timer will not execute
   142  // any more calls to keephouse once it has returned.
   143  func (tm *Timer) Stop() {
   144  	tm.mu.Lock()
   145  	defer tm.mu.Unlock()
   146  	if tm.running {
   147  		tm.msg <- timerStop
   148  		tm.running = false
   149  	}
   150  }
   151  
   152  // Interval returns the current interval.
   153  func (tm *Timer) Interval() time.Duration {
   154  	return tm.interval.Get()
   155  }