Files
gocryptotrader/exchanges/orderbook/node.go
Adrian Gallagher f0d45aa1d2 golangci-lint/CI: Bump versions and introduce new linters (#798)
* golangci-lint/CI: Bump versions

Fix remaining linter issues

* Specifically set AppVeyor version

* Fix the infamous typos 👀

* Add go env cmd to AppVeyor

* Add go version cmd to AppVeyor

* Specify AppVeyor image, adjust linters

* Update go get to go install due to deprecation

* Bump golangci-lint timeout time for AppVeyor

* Change NW contract to NQ

* Address nitters

* GetRandomPair -> Pair{}

* Address nits

* Address time nitterinos plus additional tweaks

* More time inception upgrades!

* Bending time and space
2021-10-14 16:38:53 +11:00

135 lines
3.6 KiB
Go

package orderbook
import (
"sync/atomic"
"time"
)
const (
neutral uint32 = iota
active
)
var (
defaultInterval = time.Minute
defaultAllowance = time.Second * 30
)
// Node defines a linked list node for an orderbook item
type Node struct {
Value Item
Next *Node
Prev *Node
// Denotes time pushed to stack, this will influence cleanup routine when
// there is a pause or minimal actions during period
shelved time.Time
}
// stack defines a FILO list of reusable nodes
type stack struct {
nodes []*Node
sema uint32
count int32
}
// newStack returns a ptr to a new stack instance, also starts the cleaning
// service
func newStack() *stack {
s := &stack{}
go s.cleaner()
return s
}
// now defines a time which is now to ensure no other values get passed in
type now time.Time
// getNow returns the time at which it is called
func getNow() now {
return now(time.Now())
}
// Push pushes a node pointer into the stack to be reused the time is passed in
// to allow for inlining which sets the time at which the node is theoretically
// pushed to a stack.
func (s *stack) Push(n *Node, tn now) {
if !atomic.CompareAndSwapUint32(&s.sema, neutral, active) {
// Stack is in use, for now we can dereference pointer
return
}
// Adds a time when its placed back on to stack.
n.shelved = time.Time(tn)
n.Next = nil
n.Prev = nil
n.Value = Item{}
// Allows for resize when overflow TODO: rethink this
s.nodes = append(s.nodes[:s.count], n)
s.count++
atomic.StoreUint32(&s.sema, neutral)
}
// Pop returns the last pointer off the stack and reduces the count and if empty
// will produce a lovely fresh node
func (s *stack) Pop() *Node {
if !atomic.CompareAndSwapUint32(&s.sema, neutral, active) {
// Stack is in use, for now we can allocate a new node pointer
return &Node{}
}
if s.count == 0 {
// Create an empty node when no nodes are in slice or when cleaning
// service is running
atomic.StoreUint32(&s.sema, neutral)
return &Node{}
}
s.count--
n := s.nodes[s.count]
atomic.StoreUint32(&s.sema, neutral)
return n
}
// cleaner (POC) runs to the defaultTimer to clean excess nodes (nodes not being
// utilised) TODO: Couple time parameters to check for a reduction in activity.
// Add in counter per second function (?) so if there is a lot of activity don't
// inhibit stack performance.
func (s *stack) cleaner() {
tt := time.NewTimer(defaultInterval)
sleeperino:
for range tt.C {
if !atomic.CompareAndSwapUint32(&s.sema, neutral, active) {
// Stack is in use, reset timer to zero to recheck for neutral state.
tt.Reset(0)
continue
}
// As the old nodes are going to be left justified on this slice we
// should just be able to shift the nodes that are still within time
// allowance all the way to the left. Not going to resize capacity
// because if it can get this big, it might as well stay this big.
// TODO: Test and rethink if sizing is an issue
for x := int32(0); x < s.count; x++ {
if time.Since(s.nodes[x].shelved) > defaultAllowance {
// Old node found continue
continue
}
// First good node found, everything to the left of this on the
// slice can be reassigned
var counter int32
for y := int32(0); y+x < s.count; y++ { // Go through good nodes
// Reassign
s.nodes[y] = s.nodes[y+x]
// Add to the changed counter to remove from main
// counter
counter++
}
s.count -= counter
atomic.StoreUint32(&s.sema, neutral)
tt.Reset(defaultInterval)
continue sleeperino
}
// Nodes are old, flush entirety.
s.count = 0
atomic.StoreUint32(&s.sema, neutral)
tt.Reset(defaultInterval)
}
}