-
Notifications
You must be signed in to change notification settings - Fork 153
/
count.go
162 lines (129 loc) · 3.42 KB
/
count.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package count
import (
"math"
"sync"
"sync/atomic"
)
// Counter is an interface for counting.
// It contains counting data as long as a type
// implements all the methods in the interface.
type Counter interface {
// Get returns the current count.
Get() float64
// Add adds the delta value to the counter.
Add(delta float64)
}
// NaiveCounter counts in a naive way. Do not use this with concurrency.
// It will cause race conditions. This is not thread-safe.
type NaiveCounter float64
func (c *NaiveCounter) Get() float64 {
// return (*c).(float64)
// (X) (*c).(float64) (non-interface type NaiveCounter on left)
return float64(*c)
}
func (c *NaiveCounter) Add(delta float64) {
*c += NaiveCounter(delta)
}
// MutexCounter implements Counter with sync.Mutex.
type MutexCounter struct {
mu sync.Mutex // guards the following
value float64
}
func (c *MutexCounter) Get() float64 {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
func (c *MutexCounter) Add(delta float64) {
c.mu.Lock()
defer c.mu.Unlock()
c.value += delta
}
// RWMutexCounter implements Counter with sync.RWMutex.
type RWMutexCounter struct {
mu sync.RWMutex // guards the following sync.
value float64
}
func (c *RWMutexCounter) Get() float64 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.value
}
func (c *RWMutexCounter) Add(delta float64) {
c.mu.Lock()
defer c.mu.Unlock()
c.value += delta
}
// AtomicIntCounter implements Counter with atomic package.
// Go has only int64 atomic variable.
// This truncates float value into integer.
type AtomicIntCounter int64
func (c *AtomicIntCounter) Get() float64 {
return float64(atomic.LoadInt64((*int64)(c)))
}
// Add ignores the non-integer part of delta.
func (c *AtomicIntCounter) Add(delta float64) {
atomic.AddInt64((*int64)(c), int64(delta))
}
// AtomicCounter implements Counter with atomic package.
// Go has only int64 atomic variable.
// This uses math.Float64frombits package for the floating
// point number corresponding the IEEE 754 binary representation
type AtomicCounter uint64
func (c *AtomicCounter) Get() float64 {
return math.Float64frombits(atomic.LoadUint64((*uint64)(c)))
}
// Add ignores the non-integer part of delta.
func (c *AtomicCounter) Add(delta float64) {
for {
oldBits := atomic.LoadUint64((*uint64)(c))
newBits := math.Float64bits(math.Float64frombits(oldBits) + delta)
if atomic.CompareAndSwapUint64((*uint64)(c), oldBits, newBits) {
return
}
}
}
// ChannelCounter counts through channels.
type ChannelCounter struct {
valueChan chan float64
deltaChan chan float64
done chan struct{}
}
func NewChannelCounter(buf int) *ChannelCounter {
c := &ChannelCounter{
make(chan float64),
make(chan float64, buf), // only buffer the deltaChan
make(chan struct{}),
}
go c.Run()
return c
}
func (c *ChannelCounter) Run() {
var value float64
for {
// "select" statement chooses which of a set of
// possible send or receive operations will proceed.
select {
case delta := <-c.deltaChan:
value += delta
case <-c.done:
return
case c.valueChan <- value:
// Do nothing.
// If there is no default case, the "select" statement
// blocks until at least one of the communications can proceed.
}
}
}
func (c *ChannelCounter) Get() float64 {
return <-c.valueChan
}
func (c *ChannelCounter) Add(delta float64) {
c.deltaChan <- delta
}
func (c *ChannelCounter) Done() {
c.done <- struct{}{}
}
func (c *ChannelCounter) Close() {
close(c.deltaChan)
}