-
Notifications
You must be signed in to change notification settings - Fork 5
/
gotemplate_nodeTreePool.go
177 lines (149 loc) · 4.72 KB
/
gotemplate_nodeTreePool.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Code generated by gotemplate. DO NOT EDIT.
package orderbook
import (
"runtime"
"sync/atomic"
"golang.org/x/sys/cpu"
)
// template type ItemPool(PoolItem)
// A simple order pool.
type nodeTreePool struct {
ch *itemChanNodeTreePool
}
func newNodeTreePool(maxSize uint64) *nodeTreePool {
ch := newItemChanNodeTreePool(maxSize)
p := &nodeTreePool{
ch: ch,
}
for !ch.IsFull() {
ch.Put(&nodeTree{})
}
return p
}
func (p *nodeTreePool) Get() *nodeTree {
if p.ch.IsEmpty() {
return &nodeTree{}
}
return p.ch.Read()
}
func (p *nodeTreePool) Put(o *nodeTree) {
if o == nil {
return
}
if p.ch.IsFull() {
// Leave it to GC
return
}
p.ch.Put(o)
}
// CONTENT BELOW GENERATED USING
// go:generate gotemplate "github.com/geseq/fastchan" itemChan(*PoolItem)
// --------------------------------------------------------------------
//
//
// Copyright 2012 Darren Elwood <darren@textnode.com> http://www.textnode.com @textnode
// Copyright 2021 E Sequeira
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A fast chan replacement based on textnode/gringo
//
// N.B. To see the performance benefits of gringo versus Go's channels, you must have multiple goroutines
// and GOMAXPROCS > 1.
// Known Limitations:
//
// *) At most (2^64)-2 items can be written to the queue.
// *) The size of the queue must be a power of 2.
//
// Suggestions:
//
// *) If you have enough cores you can change from runtime.Gosched() to a busy loop.
//
// FastChan is a minimalist queue replacement for channels with higher throughput
type itemChanNodeTreePool struct {
// The padding members 1 to 5 below are here to ensure each item is on a separate cache line.
// This prevents false sharing and hence improves performance.
_ cpu.CacheLinePad
indexMask uint64
_ cpu.CacheLinePad
lastCommittedIndex uint64
_ cpu.CacheLinePad
nextFreeIndex uint64
_ cpu.CacheLinePad
readerIndex uint64
_ cpu.CacheLinePad
contents []*nodeTree
_ cpu.CacheLinePad
}
// NewFastChan creates a new channel
func newItemChanNodeTreePool(size uint64) *itemChanNodeTreePool {
size = roundUpNextPowerOfTwoItemChanNodeTreePool(size)
return &itemChanNodeTreePool{
lastCommittedIndex: 0,
nextFreeIndex: 0,
readerIndex: 0,
indexMask: size - 1,
contents: make([]*nodeTree, size),
}
}
// Put writes a CacheItem to the front of the channel
func (c *itemChanNodeTreePool) Put(value *nodeTree) {
//Wait for reader to catch up, so we don't clobber a slot which it is (or will be) reading
for atomic.LoadUint64(&c.nextFreeIndex)+1 > (atomic.LoadUint64(&c.readerIndex) + c.indexMask) {
runtime.Gosched()
}
var myIndex = atomic.AddUint64(&c.nextFreeIndex, 1)
//Write the item into it's slot
c.contents[myIndex&c.indexMask] = value
//Increment the lastCommittedIndex so the item is available for reading
for !atomic.CompareAndSwapUint64(&c.lastCommittedIndex, myIndex-1, myIndex) {
runtime.Gosched()
}
}
// Read reads and removes a CacheItem from the back of the channel
func (c *itemChanNodeTreePool) Read() *nodeTree {
//If reader has out-run writer, wait for a value to be committed
for atomic.LoadUint64(&c.readerIndex)+1 > atomic.LoadUint64(&c.lastCommittedIndex) {
runtime.Gosched()
}
var myIndex = atomic.AddUint64(&c.readerIndex, 1)
return c.contents[myIndex&c.indexMask]
}
// Empty the channel
func (c *itemChanNodeTreePool) Empty() {
c.lastCommittedIndex = 0
c.nextFreeIndex = 0
c.readerIndex = 0
}
// Size gets the size of the contents in the channel buffer
func (c *itemChanNodeTreePool) Size() uint64 {
return atomic.LoadUint64(&c.lastCommittedIndex) - atomic.LoadUint64(&c.readerIndex)
}
// IsEmpty checks if the channel is empty
func (c *itemChanNodeTreePool) IsEmpty() bool {
return atomic.LoadUint64(&c.readerIndex) >= atomic.LoadUint64(&c.lastCommittedIndex)
}
// IsFull checks if the channel is full
func (c *itemChanNodeTreePool) IsFull() bool {
return atomic.LoadUint64(&c.nextFreeIndex) >= (atomic.LoadUint64(&c.readerIndex) + c.indexMask)
}
func roundUpNextPowerOfTwoItemChanNodeTreePool(v uint64) uint64 {
v--
v |= v >> 1
v |= v >> 2
v |= v >> 4
v |= v >> 8
v |= v >> 16
v |= v >> 32
v++
return v
}