-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathat_most.go
147 lines (137 loc) · 3.63 KB
/
at_most.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
package routines
import (
"sync"
"time"
)
// OnceAtMost ensures every calls to f() is executed, but not too fast
//
// It guarantees:
// - run the function as many times as you call
// - only one call is running at the same time
// - no more than once within the duration.
//
// Time is recorded before calling real function, which means the duration includes
// function execution time.
//
// Say you have a f() prints "test", and costs 0.1s each call
//
// x := OnceAtMost(time.Second, f)
// go x()
// go x()
// go x()
//
// You'll see a "test" every second for 3 seconds.
//
// If x() is not called in another goroutine, it acts much like RunAtLeast
//
// x() // blocks 1s
// x() // blocks 1s
func OnceAtMost(dur time.Duration, f func() error) func() error {
lock := new(sync.Mutex)
last := time.Now().Add(0 - dur)
return func() error {
lock.Lock()
defer lock.Unlock()
if d := time.Since(last); d <= dur {
time.Sleep(dur - d)
}
last = time.Now()
return f()
}
}
// OnceSuccessAtMost is identical to OnceAtMost, but only successful call is ensured
//
// Say you have a f() prints "test" no matter success or failed, and costs 0.1s each call
//
// x := OnceAtMost(time.Second, f)
// go x() // assumes it failed
// go x() // assumes it failed
// go x() // assumes it succeeded
//
// You'll see:
//
// * a "test" at 0.1s
// * another "test" at 0.2s (0.1s after previous "test")
// * another "test" at 1.2s (1s after previous "test")
func OnceSuccessAtMost(dur time.Duration, f func() error) func() error {
lock := new(sync.Mutex)
last := time.Now().Add(0 - dur)
return func() error {
lock.Lock()
defer lock.Unlock()
if d := time.Since(last); d <= dur {
time.Sleep(dur - d)
}
now := time.Now()
ret := f()
if ret == nil {
last = now
}
return ret
}
}
// OnceWithin is identical to OnceAtMost, but calls within duration are ignored
//
// Say you have a f() prints "test", and costs 0.5s each call
//
// x := OnceWithin(time.Second, f)
// go x() // this should be executed and print "test"
// go x() // this should be ignored
// go x() // this should also be ignored
// time.Sleep(time.Second)
// go x() // this should be executed and print "test"
func OnceWithin(dur time.Duration, f func() error) func() error {
lock := new(sync.RWMutex)
last := time.Now().Add(0 - dur)
return func() error {
lock.RLock()
if d := time.Since(last); d <= dur {
lock.RUnlock()
return nil
}
lock.RUnlock()
lock.Lock()
defer lock.Unlock()
if d := time.Since(last); d <= dur {
return nil
}
last = time.Now()
return f()
}
}
// OnceSuccessWithin is identical to OnceWithin, but only success call is ensured
//
// Say you have a f() prints "test", and costs 0.5s each call
//
// x := OnceSuccessWithin(time.Second, f)
// go x() // f executed, assumes it failed
// go x() // f is executed, assumes it succeeded, prints "test"
// go x() // this should be ignored
// time.Sleep(time.Second)
// go x() // f is executed
func OnceSuccessWithin(dur time.Duration, f func() error) func() error {
lock := new(sync.RWMutex)
last := time.Now().Add(0 - dur)
return func() error {
lock.RLock()
if d := time.Since(last); d <= dur {
lock.RUnlock()
return nil
}
lock.RUnlock()
lock.Lock()
defer lock.Unlock()
if d := time.Since(last); d <= dur {
return nil
}
now := time.Now()
ret := f()
if ret == nil {
last = now
}
return ret
}
}