Skip to content

Commit

Permalink
issue-34
Browse files Browse the repository at this point in the history
  • Loading branch information
enriquebris committed Nov 17, 2022
1 parent ea2e52e commit 082dbc7
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 8 deletions.
20 changes: 14 additions & 6 deletions fixed_fifo_queue.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package goconcurrentqueue

import "context"
import (
"context"
)

// Fixed capacity FIFO (First In First Out) concurrent queue
type FixedFIFO struct {
Expand Down Expand Up @@ -35,11 +37,11 @@ func (st *FixedFIFO) Enqueue(value interface{}) error {
// verify whether it is possible to notify the listener (it could be the listener is no longer
// available because the context expired: DequeueOrWaitForNextElementContext)
select {
// sends the element through the listener's channel instead of enqueueing it
case listener <- value:
default:
// push the element into the queue instead of sending it through the listener's channel (which is not available at this moment)
return st.enqueueIntoQueue(value)
// sends the element through the listener's channel instead of enqueueing it
case listener <- value:
default:
// push the element into the queue instead of sending it through the listener's channel (which is not available at this moment)
return st.enqueueIntoQueue(value)
}

default:
Expand Down Expand Up @@ -114,6 +116,12 @@ func (st *FixedFIFO) DequeueOrWaitForNextElementContext(ctx context.Context) (in
return item, nil
case <-ctx.Done():
return nil, ctx.Err()
// try again to get the element from the regular queue (in case waitChan doesn't provide any item)
case value, ok := <-st.queue:
if ok {
return value, nil
}
return nil, NewQueueError(QueueErrorCodeInternalChannelClosed, "internal channel is closed")
}
default:
// too many watchers (waitForNextElementChanCapacity) enqueued waiting for next elements
Expand Down
39 changes: 37 additions & 2 deletions fixed_fifo_queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (suite *FixedFIFOTestSuite) TestEnqueueFullCapacitySingleGR() {
func (suite *FixedFIFOTestSuite) TestEnqueueListenerToExpireSingleGR() {
var (
uselessChan = make(chan interface{})
value = "my-test-value"
value = "my-test-value"
)

// let Enqueue knows there is a channel to send the next item instead of enqueueing it into the queue
Expand All @@ -98,6 +98,7 @@ func (suite *FixedFIFOTestSuite) TestEnqueueListenerToExpireSingleGR() {
// TestEnqueueLenMultipleGR enqueues elements concurrently
//
// Detailed steps:
//
// 1 - Enqueue totalGRs concurrently (from totalGRs different GRs)
// 2 - Verifies the len, it should be equal to totalGRs
// 3 - Verifies that all elements from 0 to totalGRs were enqueued
Expand Down Expand Up @@ -269,6 +270,7 @@ func (suite *FixedFIFOTestSuite) TestDequeueClosedChannelSingleGR() {
// TestDequeueMultipleGRs dequeues elements concurrently
//
// Detailed steps:
//
// 1 - Enqueues totalElementsToEnqueue consecutive integers
// 2 - Dequeues totalElementsToDequeue concurrently from totalElementsToDequeue GRs
// 3 - Verifies the final len, should be equal to totalElementsToEnqueue - totalElementsToDequeue
Expand Down Expand Up @@ -376,6 +378,39 @@ func (suite *FixedFIFOTestSuite) TestDequeueOrWaitForNextElementWithEmptyQueue()
}
}

// calling DequeueOrWaitForNextElement with empty queue, then adding an item directly into queue's internal channel
func (suite *FixedFIFOTestSuite) TestDequeueOrWaitForNextElementWithStuckWaitChan() {
var (
dummyValue = "dummyValue"
doneChan = make(chan struct{})
)

// consumer
go func(queue *FixedFIFO, expectedValue interface{}, done chan struct{}) {
item, err := queue.DequeueOrWaitForNextElement()
suite.NoError(err)
suite.Equal(expectedValue, item)

done <- struct{}{}
}(suite.fifo, dummyValue, doneChan)

// a second should be enough for the consumer to start consuming ...
time.Sleep(time.Second)

// add an item (enqueue) directly into queue's internal channel
suite.fifo.queue <- dummyValue

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

select {
case <-doneChan:

case <-ctx.Done():
suite.Fail("too much time waiting ...")
}
}

// single GR calling DequeueOrWaitForNextElement (WaitForNextElementChanCapacity + 1) times, last one should return error
func (suite *FixedFIFOTestSuite) TestDequeueOrWaitForNextElementWithFullWaitingChannel() {
// enqueue WaitForNextElementChanCapacity listeners to future enqueued elements
Expand Down Expand Up @@ -554,4 +589,4 @@ func (suite *FixedFIFOTestSuite) TestContextAlreadyCanceled() {
case <-time.After(2 * time.Second):
suite.Fail("DequeueOrWaitForNextElementContext did not return immediately after context was canceled")
}
}
}

0 comments on commit 082dbc7

Please sign in to comment.