2023-03-02 09:53:12 +00:00
|
|
|
package pq_test
|
2023-03-01 04:33:22 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"math/rand"
|
2023-03-02 03:31:36 +00:00
|
|
|
"runtime"
|
2023-03-01 04:33:22 +00:00
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
|
2023-03-03 05:35:17 +00:00
|
|
|
"gogs.humancabbage.net/sam/priorityq"
|
2023-03-02 09:53:12 +00:00
|
|
|
"gogs.humancabbage.net/sam/priorityq/pq"
|
2023-03-01 04:33:22 +00:00
|
|
|
)
|
|
|
|
|
2023-03-02 03:22:37 +00:00
|
|
|
func TestRecvHighestFirst(t *testing.T) {
|
2023-03-01 04:33:22 +00:00
|
|
|
t.Parallel()
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](8)
|
2023-03-02 03:22:37 +00:00
|
|
|
q.Send(4, 4)
|
|
|
|
q.Send(2, 2)
|
|
|
|
q.Send(1, 1)
|
|
|
|
q.Send(5, 5)
|
|
|
|
q.Send(7, 7)
|
|
|
|
q.Send(8, 8)
|
|
|
|
q.Send(3, 3)
|
|
|
|
q.Send(6, 6)
|
2023-03-01 04:33:22 +00:00
|
|
|
checkRecv := func(n int) {
|
2023-03-02 03:22:37 +00:00
|
|
|
if _, v, _ := q.Recv(); v != n {
|
2023-03-01 04:33:22 +00:00
|
|
|
t.Errorf("popped %d, expected %d", v, n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
checkRecv(8)
|
2023-03-02 03:22:37 +00:00
|
|
|
checkRecv(7)
|
|
|
|
checkRecv(6)
|
|
|
|
checkRecv(5)
|
2023-03-01 04:33:22 +00:00
|
|
|
checkRecv(4)
|
2023-03-02 03:22:37 +00:00
|
|
|
checkRecv(3)
|
|
|
|
checkRecv(2)
|
|
|
|
checkRecv(1)
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSendClosedPanic(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r == nil {
|
|
|
|
t.Errorf("sending to closed queue did not panic")
|
|
|
|
}
|
|
|
|
}()
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](4)
|
2023-03-01 04:33:22 +00:00
|
|
|
q.Close()
|
2023-03-02 03:22:37 +00:00
|
|
|
q.Send(1, 1)
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRecvClosed(t *testing.T) {
|
|
|
|
t.Parallel()
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](4)
|
2023-03-02 03:22:37 +00:00
|
|
|
q.Send(1, 1)
|
2023-03-01 04:33:22 +00:00
|
|
|
q.Close()
|
2023-03-02 03:22:37 +00:00
|
|
|
_, _, ok := q.Recv()
|
2023-03-01 04:33:22 +00:00
|
|
|
if !ok {
|
|
|
|
t.Errorf("queue should have item to receive")
|
|
|
|
}
|
2023-03-02 03:22:37 +00:00
|
|
|
_, _, ok = q.Recv()
|
2023-03-01 04:33:22 +00:00
|
|
|
if ok {
|
|
|
|
t.Errorf("queue should be closed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 09:53:12 +00:00
|
|
|
func TestDoubleClose(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
q := pq.Make[int, int](4)
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r == nil {
|
|
|
|
t.Errorf("closing a closed queue did not panic")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
q.Close()
|
|
|
|
q.Close()
|
|
|
|
}
|
|
|
|
|
2023-03-01 04:33:22 +00:00
|
|
|
func TestTrySendRecv(t *testing.T) {
|
|
|
|
t.Parallel()
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](4)
|
2023-03-02 03:22:37 +00:00
|
|
|
assumeSendOk := func(n int) {
|
2023-03-03 05:35:17 +00:00
|
|
|
err := q.TrySend(n, n)
|
|
|
|
if err != nil {
|
2023-03-01 04:33:22 +00:00
|
|
|
t.Errorf("expected to be able to send")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assumeRecvOk := func(expected int) {
|
2023-03-03 05:35:17 +00:00
|
|
|
_, actual, err := q.TryRecv()
|
|
|
|
if err != nil {
|
2023-03-01 04:33:22 +00:00
|
|
|
t.Errorf("expected to be able to receive")
|
|
|
|
}
|
|
|
|
if actual != expected {
|
|
|
|
t.Errorf("expected %d, got %d", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
2023-03-02 03:22:37 +00:00
|
|
|
assumeSendOk(1)
|
|
|
|
assumeSendOk(2)
|
|
|
|
assumeSendOk(3)
|
|
|
|
assumeSendOk(4)
|
2023-03-03 05:35:17 +00:00
|
|
|
err := q.TrySend(5, 5)
|
|
|
|
if err == nil {
|
2023-03-02 03:22:37 +00:00
|
|
|
t.Errorf("expected queue to be full")
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
assumeRecvOk(4)
|
2023-03-02 03:22:37 +00:00
|
|
|
assumeRecvOk(3)
|
|
|
|
assumeRecvOk(2)
|
|
|
|
assumeRecvOk(1)
|
2023-03-01 04:33:22 +00:00
|
|
|
|
2023-03-03 05:35:17 +00:00
|
|
|
_, _, err = q.TryRecv()
|
|
|
|
if err != priorityq.ErrEmpty {
|
2023-03-01 04:33:22 +00:00
|
|
|
t.Errorf("expected queue to be empty")
|
|
|
|
}
|
2023-03-03 05:35:17 +00:00
|
|
|
q.Close()
|
|
|
|
_, _, err = q.TryRecv()
|
|
|
|
if err != priorityq.ErrClosed {
|
|
|
|
t.Errorf("expected queue to be closed ")
|
|
|
|
}
|
|
|
|
err = q.TrySend(1, 1)
|
|
|
|
if err != priorityq.ErrClosed {
|
|
|
|
t.Errorf("expected queue to be closed ")
|
|
|
|
}
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestConcProducerConsumer(t *testing.T) {
|
|
|
|
t.Parallel()
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](4)
|
2023-03-01 04:33:22 +00:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
produceDone := make(chan struct{})
|
|
|
|
wg.Add(2)
|
|
|
|
go func() {
|
|
|
|
for i := 0; i < 10000; i++ {
|
2023-03-02 03:22:37 +00:00
|
|
|
q.Send(rand.Int(), i)
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
close(produceDone)
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
ok := true
|
|
|
|
for ok {
|
2023-03-02 03:22:37 +00:00
|
|
|
_, _, ok = q.Recv()
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
<-produceDone
|
|
|
|
t.Logf("producer done, closing channel")
|
|
|
|
q.Close()
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
2024-08-22 07:53:48 +00:00
|
|
|
func TestIter(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
q := pq.Make[int, int](4)
|
|
|
|
q.Send(4, 0)
|
|
|
|
q.Send(3, 1)
|
|
|
|
q.Send(2, 2)
|
|
|
|
q.Send(1, 3)
|
|
|
|
q.Close()
|
|
|
|
i := 0
|
|
|
|
for v := range q.Iter() {
|
|
|
|
if v != i {
|
|
|
|
t.Errorf("expected %d, got %d", i, v)
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
// to test yield() returning false
|
|
|
|
q = pq.Make[int, int](4)
|
|
|
|
q.Send(1, 3)
|
|
|
|
for _ = range q.Iter() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-01 04:33:22 +00:00
|
|
|
func BenchmarkSend(b *testing.B) {
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](b.N)
|
2023-03-02 03:22:37 +00:00
|
|
|
// randomize priorities to get amortized cost per op
|
|
|
|
ps := make([]int, b.N)
|
2023-03-01 04:33:22 +00:00
|
|
|
for i := 0; i < b.N; i++ {
|
2023-03-02 03:22:37 +00:00
|
|
|
ps[i] = rand.Int()
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
2023-03-02 03:22:37 +00:00
|
|
|
q.Send(ps[i], i)
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkRecv(b *testing.B) {
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](b.N)
|
2023-03-02 03:22:37 +00:00
|
|
|
// randomize priorities to get amortized cost per op
|
2023-03-01 04:33:22 +00:00
|
|
|
for i := 0; i < b.N; i++ {
|
2023-03-02 03:22:37 +00:00
|
|
|
q.Send(rand.Int(), i)
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
q.Recv()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 03:22:37 +00:00
|
|
|
func BenchmarkConcSendRecv(b *testing.B) {
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](b.N)
|
2023-03-02 03:22:37 +00:00
|
|
|
// randomize priorities to get amortized cost per op
|
|
|
|
ps := make([]int, b.N)
|
2023-03-01 04:33:22 +00:00
|
|
|
for i := 0; i < b.N; i++ {
|
2023-03-02 03:22:37 +00:00
|
|
|
ps[i] = rand.Int()
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(2)
|
|
|
|
start := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
<-start
|
|
|
|
for i := 0; i < b.N; i++ {
|
2023-03-02 03:22:37 +00:00
|
|
|
q.Send(ps[i], i)
|
2023-03-01 04:33:22 +00:00
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
<-start
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
q.Recv()
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
b.ResetTimer()
|
|
|
|
close(start)
|
|
|
|
wg.Wait()
|
|
|
|
}
|
2023-03-02 03:31:36 +00:00
|
|
|
|
|
|
|
func BenchmarkHighContention(b *testing.B) {
|
2023-03-02 09:53:12 +00:00
|
|
|
q := pq.Make[int, int](b.N)
|
2023-03-02 03:31:36 +00:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
start := make(chan struct{})
|
|
|
|
done := make(chan struct{})
|
|
|
|
numProducers := runtime.NumCPU()
|
|
|
|
sendsPerProducer := b.N / numProducers
|
|
|
|
wg.Add(numProducers)
|
|
|
|
for i := 0; i < numProducers; i++ {
|
|
|
|
go func() {
|
|
|
|
ps := make([]int, sendsPerProducer)
|
|
|
|
for i := 0; i < sendsPerProducer; i++ {
|
|
|
|
ps[i] = rand.Int()
|
|
|
|
}
|
|
|
|
<-start
|
|
|
|
for i := 0; i < sendsPerProducer; i++ {
|
|
|
|
q.Send(ps[i], 1)
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
ok := true
|
|
|
|
for ok {
|
|
|
|
_, _, ok = q.Recv()
|
|
|
|
}
|
|
|
|
close(done)
|
|
|
|
}()
|
|
|
|
b.ResetTimer()
|
|
|
|
close(start)
|
|
|
|
wg.Wait()
|
|
|
|
q.Close()
|
|
|
|
<-done
|
|
|
|
}
|