Various improvements.
* Expand README.md, provide benchmark results. * Add docs, benchmarks for binheap and circ packages. * Add methods Len() and Capacity(). * Change *sync.Cond to sync.Cond. * TryRecv() and TrySend() distinguish empty and closed errors. * Improve test coverage. * Add basic Makefile. * Fix documentation mistakes.
This commit is contained in:
31
pq/lib.go
31
pq/lib.go
@@ -12,7 +12,7 @@
|
||||
// _, word1, _ := pq.Recv()
|
||||
// _, word2, _ := pq.Recv()
|
||||
// fmt.Println(word1, word2)
|
||||
// pq.Close()
|
||||
// q.Close()
|
||||
// // Output: hello world
|
||||
//
|
||||
// # Implementation
|
||||
@@ -26,6 +26,7 @@ package pq
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"gogs.humancabbage.net/sam/priorityq"
|
||||
"gogs.humancabbage.net/sam/priorityq/binheap"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
@@ -41,16 +42,16 @@ func Make[P constraints.Ordered, T any](cap int) Q[P, T] {
|
||||
s := &state[P, T]{
|
||||
heap: heap,
|
||||
}
|
||||
s.canRecv = sync.NewCond(&s.mu)
|
||||
s.canSend = sync.NewCond(&s.mu)
|
||||
s.canRecv = sync.Cond{L: &s.mu}
|
||||
s.canSend = sync.Cond{L: &s.mu}
|
||||
return Q[P, T]{s}
|
||||
}
|
||||
|
||||
type state[P constraints.Ordered, T any] struct {
|
||||
mu sync.Mutex
|
||||
heap binheap.H[P, T]
|
||||
canSend *sync.Cond
|
||||
canRecv *sync.Cond
|
||||
canSend sync.Cond
|
||||
canRecv sync.Cond
|
||||
closed bool
|
||||
}
|
||||
|
||||
@@ -116,16 +117,21 @@ func (s *state[P, T]) Send(priority P, value T) {
|
||||
//
|
||||
// This returns both the item itself and the its assigned priority.
|
||||
//
|
||||
// If the attempt succeeds, the returned bool is true. Otherwise, it is false.
|
||||
func (s *state[P, T]) TryRecv() (priority P, value T, ok bool) {
|
||||
// The error indicates whether the attempt succeeded, the queue is empty, or
|
||||
// the queue is closed.
|
||||
func (s *state[P, T]) TryRecv() (priority P, value T, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.heap.CanExtract() {
|
||||
priority, value = s.heap.Extract()
|
||||
ok = true
|
||||
s.canSend.Broadcast()
|
||||
return
|
||||
}
|
||||
if s.closed {
|
||||
err = priorityq.ErrClosed
|
||||
} else {
|
||||
err = priorityq.ErrEmpty
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -133,13 +139,16 @@ func (s *state[P, T]) TryRecv() (priority P, value T, ok bool) {
|
||||
//
|
||||
// This method does not block. If there is space in the buffer, it returns
|
||||
// true. If the buffer is full, it returns false.
|
||||
func (s *state[P, T]) TrySend(priority P, value T) bool {
|
||||
func (s *state[P, T]) TrySend(priority P, value T) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.closed {
|
||||
return priorityq.ErrClosed
|
||||
}
|
||||
if !s.heap.CanInsert() {
|
||||
return false
|
||||
return priorityq.ErrFull
|
||||
}
|
||||
s.heap.Insert(priority, value)
|
||||
s.canRecv.Broadcast()
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"gogs.humancabbage.net/sam/priorityq"
|
||||
"gogs.humancabbage.net/sam/priorityq/pq"
|
||||
)
|
||||
|
||||
@@ -78,14 +79,14 @@ func TestTrySendRecv(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := pq.Make[int, int](4)
|
||||
assumeSendOk := func(n int) {
|
||||
ok := q.TrySend(n, n)
|
||||
if !ok {
|
||||
err := q.TrySend(n, n)
|
||||
if err != nil {
|
||||
t.Errorf("expected to be able to send")
|
||||
}
|
||||
}
|
||||
assumeRecvOk := func(expected int) {
|
||||
_, actual, ok := q.TryRecv()
|
||||
if !ok {
|
||||
_, actual, err := q.TryRecv()
|
||||
if err != nil {
|
||||
t.Errorf("expected to be able to receive")
|
||||
}
|
||||
if actual != expected {
|
||||
@@ -96,8 +97,8 @@ func TestTrySendRecv(t *testing.T) {
|
||||
assumeSendOk(2)
|
||||
assumeSendOk(3)
|
||||
assumeSendOk(4)
|
||||
ok := q.TrySend(5, 5)
|
||||
if ok {
|
||||
err := q.TrySend(5, 5)
|
||||
if err == nil {
|
||||
t.Errorf("expected queue to be full")
|
||||
}
|
||||
assumeRecvOk(4)
|
||||
@@ -105,10 +106,19 @@ func TestTrySendRecv(t *testing.T) {
|
||||
assumeRecvOk(2)
|
||||
assumeRecvOk(1)
|
||||
|
||||
_, _, ok = q.TryRecv()
|
||||
if ok {
|
||||
_, _, err = q.TryRecv()
|
||||
if err != priorityq.ErrEmpty {
|
||||
t.Errorf("expected queue to be empty")
|
||||
}
|
||||
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 ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcProducerConsumer(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user