...
1 package utils
2
3 import (
4 "flag"
5 "fmt"
6 "sort"
7 "sync"
8 "time"
9 )
10
11 var (
12 debug_pt = flag.Bool("ge_debug_periodictimer", false, "debug the periodic timer")
13 )
14
15 type PeriodicTimer struct {
16 secs []time.Duration
17 started time.Time
18 callback func(*PeriodicTimer, time.Duration) error
19 thread_running bool
20 stop_request bool
21 wait_chan chan bool
22 lastSuccessfulRunSecs time.Duration
23 wasRunAtStart bool
24 lock sync.Mutex
25 runLock sync.Mutex
26 }
27
28
37 func NewPeriodicTimer(secs []time.Duration, cb func(pt *PeriodicTimer, secsLapsed time.Duration) error) *PeriodicTimer {
38 if len(secs) == 0 {
39 secs = []time.Duration{time.Duration(0)}
40 }
41 sort.Slice(secs, func(i, j int) bool {
42 return secs[i] > secs[j]
43 })
44 pt := &PeriodicTimer{callback: cb, wait_chan: make(chan bool), secs: secs}
45 return pt
46 }
47 func (pt *PeriodicTimer) Start() {
48 pt.lock.Lock()
49 pt.started = time.Now()
50 pt.lastSuccessfulRunSecs = 0
51 pt.stop_request = false
52 pt.wasRunAtStart = false
53 if !pt.thread_running {
54 go pt.timerLoop()
55 pt.thread_running = true
56 }
57 err := pt.run_callback(0)
58 if err == nil {
59 pt.wasRunAtStart = true
60 }
61 pt.lock.Unlock()
62
63 }
64 func (pt *PeriodicTimer) Stop() {
65 pt.lock.Lock()
66 pt.stop_request = true
67 pt.lock.Unlock()
68 }
69
70
71 func (pt *PeriodicTimer) Wait() {
72 <-pt.wait_chan
73 pt.stop_request = true
74 }
75
76 func (pt *PeriodicTimer) timerLoop() {
77 pt.debugf("timerloop started")
78 for {
79 if pt.stop_request {
80 break
81 }
82 time.Sleep(time.Duration(1) * time.Second)
83 if pt.stop_request {
84 break
85 }
86 pt.checkTime()
87
88 if pt.is_running_for_as_long_as_need_be() {
89 break
90 }
91 }
92
93 pt.lock.Lock()
94 pt.wait_chan <- true
95 pt.stop_request = false
96 if !pt.thread_running {
97 pt.thread_running = false
98 }
99 pt.lock.Unlock()
100 pt.debugf("timerloop finished")
101 }
102 func (pt *PeriodicTimer) is_running_for_as_long_as_need_be() bool {
103 rs := time.Since(pt.started)
104 if rs.Seconds() >= float64(pt.secs[0]) {
105 return true
106 }
107 return false
108 }
109
110
111 func (pt *PeriodicTimer) checkTime() {
112 secs := time.Since(pt.started)
113
114 period := time.Duration(0)
115 for _, r := range pt.secs {
116 if r > secs {
117 continue
118 }
119 period = r
120 break
121 }
122 pt.debugf("period=%0.1fs, lastSucc=%0.1fs, wasrun=%v", period.Seconds(), pt.lastSuccessfulRunSecs.Seconds(), pt.wasRunAtStart)
123 if period == pt.lastSuccessfulRunSecs && pt.wasRunAtStart {
124 return
125 }
126
127 err := pt.run_callback(period)
128 if err == nil {
129 pt.lastSuccessfulRunSecs = period
130 pt.wasRunAtStart = true
131 }
132 }
133 func (pt *PeriodicTimer) run_callback(period time.Duration) error {
134 pt.runLock.Lock()
135 defer pt.runLock.Unlock()
136 err := pt.callback(pt, period)
137 return err
138 }
139 func (pt *PeriodicTimer) Secs() []time.Duration {
140 return pt.secs
141 }
142 func (pt *PeriodicTimer) LastStarted() time.Time {
143 return pt.started
144 }
145
146 func (pt *PeriodicTimer) debugf(format string, args ...interface{}) {
147 if *debug_pt == false {
148 return
149 }
150 d := time.Since(pt.LastStarted()).Seconds()
151 prefix := fmt.Sprintf("[periodictimer %v, runsince=%0.1fs] ", pt.Secs(), d)
152 x := fmt.Sprintf(format, args...)
153 fmt.Println(prefix + x)
154 }
155
View as plain text