request/nonce: Refactor to simplify package and prevent consecutive mutex lock calls when accessing/setting nonce values (#1506)

* improv. timed mutex

* Add all protection back in and jankyness because races. :'(

* Add intial benchmarkeroos

* Add master benchmarks

* goodness me

* what?

* what again?

* glorious: nits

* just a swaperino instead

* clean up package nonce so that we only need to aquire mutex once

* unlock before checking master

* commentary

* wha

* more comment

* ch comment

* nonce: Allow for broad customisation externally with a ~2ns overhead

* glorious: nits maybe works?

---------

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2024-04-12 16:54:21 +10:00
committed by GitHub
parent 9657a570dd
commit e823f9edd8
12 changed files with 92 additions and 152 deletions

View File

@@ -3,39 +3,35 @@ package nonce
import (
"strconv"
"sync"
"time"
)
// UnixNano and Unix are default nonce setters
var (
UnixNano Setter = func() int64 { return time.Now().UnixNano() }
Unix Setter = func() int64 { return time.Now().Unix() }
)
// Setter is a function that returns a nonce start value.
type Setter func() int64
// Nonce struct holds the nonce value
type Nonce struct {
n int64
m sync.Mutex
}
// Get retrieves the nonce value
func (n *Nonce) Get() Value {
n.m.Lock()
defer n.m.Unlock()
return Value(n.n)
}
// GetInc increments and returns the value of the nonce
func (n *Nonce) GetInc() Value {
// GetAndIncrement returns the current nonce value and increments it. If value
// is 0, it will set the value to the current time.
func (n *Nonce) GetAndIncrement(set Setter) Value {
n.m.Lock()
defer n.m.Unlock()
if n.n == 0 {
n.n = set()
}
val := n.n
n.n++
return Value(n.n)
}
// Set sets the nonce value
func (n *Nonce) Set(val int64) {
n.m.Lock()
n.n = val
n.m.Unlock()
}
// String returns a string version of the nonce
func (n *Nonce) String() string {
return n.Get().String()
return Value(val)
}
// Value is a return type for GetValue

View File

@@ -1,62 +1,36 @@
package nonce
import (
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGet(t *testing.T) {
func TestGetAndIncrement(t *testing.T) {
var nonce Nonce
nonce.Set(112321313)
if expected, result := Value(112321313), nonce.Get(); expected != result {
t.Errorf("Expected %d got %d", expected, result)
}
}
n1 := nonce.GetAndIncrement(Unix)
assert.NotZero(t, n1)
n2 := nonce.GetAndIncrement(Unix)
assert.NotZero(t, n2)
assert.NotEqual(t, n1, n2)
func TestGetInc(t *testing.T) {
var nonce Nonce
nonce.Set(1)
if expected, result := Value(2), nonce.GetInc(); expected != result {
t.Errorf("Expected %d got %d", expected, result)
}
}
var nonce2 Nonce
n3 := nonce2.GetAndIncrement(UnixNano)
assert.NotZero(t, n3)
n4 := nonce2.GetAndIncrement(UnixNano)
assert.NotZero(t, n4)
assert.NotEqual(t, n3, n4)
func TestSet(t *testing.T) {
var nonce Nonce
nonce.Set(1)
if result, expected := nonce.Get(), Value(1); expected != result {
t.Errorf("Expected %d got %d", expected, result)
}
assert.NotEqual(t, n1, n3)
assert.NotEqual(t, n2, n4)
}
func TestString(t *testing.T) {
var nonce Nonce
nonce.Set(12312313131)
expected := "12312313131"
result := nonce.String()
if expected != result {
t.Errorf("Expected %s got %s", expected, result)
}
nonce.n = 12312313131
got := nonce.GetAndIncrement(Unix)
assert.Equal(t, "12312313131", got.String())
v := nonce.Get()
if expected != v.String() {
t.Errorf("Expected %s got %s", expected, result)
}
}
func TestNonceConcurrency(t *testing.T) {
var nonce Nonce
nonce.Set(12312)
var wg sync.WaitGroup
wg.Add(1000)
for i := 0; i < 1000; i++ {
go func() { nonce.GetInc(); wg.Done() }()
}
wg.Wait()
if expected, result := Value(12312+1000), nonce.Get(); expected != result {
t.Errorf("Expected %d got %d", expected, result)
}
got = nonce.GetAndIncrement(Unix)
assert.Equal(t, "12312313132", got.String())
}