gitlab.com/ignitionrobotics/web/ign-go@v1.0.0-rc4/queue.go (about)

     1  package ign
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  const (
     9  	// WaitForNextElementChanCapacity is being used to set the maximum capacity of listeners
    10  	WaitForNextElementChanCapacity = 1000
    11  	// dequeueOrWaitForNextElementInvokeGapTime is being used to set the time between dequeue attempts.
    12  	dequeueOrWaitForNextElementInvokeGapTime = 10
    13  )
    14  
    15  // Queue is a thread-safe data type that uses an underlying slice as a queue.
    16  // It provides a set of methods to modify the elements of the queue.
    17  // It was created to replace a go channel since channels don't let you modify their elements order.
    18  // You can push elements inside the queue using Enqueue method. They will be pushed to the back of the queue.
    19  // You can pop elements from the queue using both Dequeue() and DequeueOrWaitForNextElement().
    20  // DequeueOrWaitForNextElement() waits until the queue has any element inside to pop it out.
    21  type Queue struct {
    22  	// slice represents the actual queue.
    23  	slice []interface{}
    24  	// rwmutex represents the read-write lock for the queue.
    25  	rwmutex sync.RWMutex
    26  	// queue for watchers that will wait for next elements (if queue is empty at DequeueOrWaitForNextElement execution)
    27  	waitForNextElementChan chan chan interface{}
    28  }
    29  
    30  // NewQueue returns a new Queue instance.
    31  func NewQueue() (queue *Queue) {
    32  	queue = &Queue{}
    33  	queue.initialize()
    34  	return
    35  }
    36  
    37  // initialize initializes the queue properties.
    38  func (st *Queue) initialize() {
    39  	st.slice = make([]interface{}, 0)
    40  	st.waitForNextElementChan = make(chan chan interface{}, WaitForNextElementChanCapacity)
    41  }
    42  
    43  // Enqueue enqueues an element.
    44  func (st *Queue) Enqueue(value interface{}) {
    45  	// check if there is a listener waiting for the next element (this element)
    46  	select {
    47  	case listener := <-st.waitForNextElementChan:
    48  		// send the element through the listener's channel instead of enqueue it
    49  		select {
    50  		case listener <- value:
    51  		default:
    52  			st.enqueue(value, true)
    53  		}
    54  
    55  	default:
    56  		st.enqueue(value, true)
    57  	}
    58  }
    59  
    60  // enqueue appends a value to the queue
    61  func (st *Queue) enqueue(value interface{}, lock bool) {
    62  	if lock {
    63  		st.rwmutex.Lock()
    64  		defer st.rwmutex.Unlock()
    65  	}
    66  
    67  	st.slice = append(st.slice, value)
    68  }
    69  
    70  // Dequeue dequeues an element. Returns error if queue is locked or empty.
    71  func (st *Queue) Dequeue() (interface{}, *ErrMsg) {
    72  	st.rwmutex.Lock()
    73  	defer st.rwmutex.Unlock()
    74  
    75  	len := len(st.slice)
    76  	if len == 0 {
    77  		return nil, NewErrorMessage(ErrorQueueEmpty)
    78  	}
    79  
    80  	element := st.slice[0]
    81  	st.slice = st.slice[1:]
    82  
    83  	return element, nil
    84  }
    85  
    86  // DequeueOrWaitForNextElement dequeues an element (if exist) or waits until the next element gets enqueued and returns it.
    87  // Multiple calls to DequeueOrWaitForNextElement() would enqueue multiple "listeners" for future enqueued elements.
    88  func (st *Queue) DequeueOrWaitForNextElement() (interface{}, *ErrMsg) {
    89  	for {
    90  		// get the slice's len
    91  		st.rwmutex.Lock()
    92  		length := len(st.slice)
    93  		st.rwmutex.Unlock()
    94  
    95  		if length == 0 {
    96  			// channel to wait for next enqueued element
    97  			waitChan := make(chan interface{})
    98  
    99  			select {
   100  			// enqueue a watcher into the watchForNextElementChannel to wait for the next element
   101  			case st.waitForNextElementChan <- waitChan:
   102  
   103  				// re-checks every i milliseconds (top: 10 times) ... the following verifies if an item was enqueued
   104  				// around the same time DequeueOrWaitForNextElement was invoked, meaning the waitChan wasn't yet sent over
   105  				// st.waitForNextElementChan
   106  				for i := 0; i < dequeueOrWaitForNextElementInvokeGapTime; i++ {
   107  					select {
   108  					case dequeuedItem := <-waitChan:
   109  						return dequeuedItem, nil
   110  					case <-time.After(time.Millisecond * time.Duration(i)):
   111  						if dequeuedItem, err := st.Dequeue(); err == nil {
   112  							return dequeuedItem, nil
   113  						}
   114  					}
   115  				}
   116  
   117  				// return the next enqueued element, if any
   118  				return <-waitChan, nil
   119  			default:
   120  				// too many watchers (waitForNextElementChanCapacity) enqueued waiting for next elements
   121  				return nil, NewErrorMessage(ErrorQueueTooManyListeners)
   122  			}
   123  		}
   124  
   125  		st.rwmutex.Lock()
   126  
   127  		// verify that at least 1 item resides on the queue
   128  		if len(st.slice) == 0 {
   129  			st.rwmutex.Unlock()
   130  			continue
   131  		}
   132  		elementToReturn := st.slice[0]
   133  		st.slice = st.slice[1:]
   134  
   135  		st.rwmutex.Unlock()
   136  		return elementToReturn, nil
   137  	}
   138  }
   139  
   140  // GetElement returns an element's value and keeps the element at the queue
   141  func (st *Queue) GetElement(index int) (interface{}, *ErrMsg) {
   142  	st.rwmutex.RLock()
   143  	defer st.rwmutex.RUnlock()
   144  
   145  	if len(st.slice) <= index {
   146  		return nil, NewErrorMessage(ErrorQueueIndexOutOfBounds)
   147  	}
   148  
   149  	return st.slice[index], nil
   150  }
   151  
   152  // GetElements returns the entire list of elements from the queue
   153  func (st *Queue) GetElements() ([]interface{}, *ErrMsg) {
   154  
   155  	st.rwmutex.RLock()
   156  	defer st.rwmutex.RUnlock()
   157  
   158  	return st.slice, nil
   159  }
   160  
   161  // GetFilteredElements returns a subset list from the queue
   162  func (st *Queue) GetFilteredElements(offset, limit int) ([]interface{}, *ErrMsg) {
   163  	st.rwmutex.Lock()
   164  	defer st.rwmutex.Unlock()
   165  
   166  	length := len(st.slice)
   167  
   168  	if length == 0 {
   169  		return st.slice, nil
   170  	}
   171  
   172  	if offset >= length || offset < 0 || limit <= 0 {
   173  		return nil, NewErrorMessage(ErrorQueueIndexOutOfBounds)
   174  	}
   175  
   176  	if (offset + limit) >= length {
   177  		limit = len(st.slice) - offset
   178  	}
   179  	low := offset
   180  	high := offset + limit
   181  	subset := st.slice[low:high]
   182  
   183  	return subset, nil
   184  }
   185  
   186  // Find returns a list of ids of the elements that match the given criteria.
   187  // Returns an empty slice if there are not elements that match.
   188  func (st *Queue) Find(criteria func(element interface{}) bool) []int {
   189  	return st.find(criteria, true)
   190  }
   191  
   192  // find returns a list of ids of the elements that match the given criteria.
   193  func (st *Queue) find(criteria func(element interface{}) bool, lock bool) (result []int) {
   194  	if lock {
   195  		st.rwmutex.Lock()
   196  		defer st.rwmutex.Unlock()
   197  	}
   198  
   199  	for id, item := range st.slice {
   200  		if criteria(item) {
   201  			result = append(result, id)
   202  		}
   203  	}
   204  
   205  	return
   206  }
   207  
   208  // FindOne returns the id from a given element.
   209  // Returns -1 if the element does not exist in the queue.
   210  func (st *Queue) FindOne(target interface{}) int {
   211  	return st.findOne(target, true)
   212  }
   213  
   214  // findOne returns the id from a given element.
   215  func (st *Queue) findOne(target interface{}, lock bool) int {
   216  	if lock {
   217  		st.rwmutex.Lock()
   218  		defer st.rwmutex.Unlock()
   219  	}
   220  
   221  	for id, item := range st.slice {
   222  		if item == target {
   223  			return id
   224  		}
   225  	}
   226  
   227  	return -1
   228  }
   229  
   230  // FindByIDs returns a list of elements of the given ids.
   231  // Returns an empty slice if there are no elements in the queue.
   232  func (st *Queue) FindByIDs(ids []int) []interface{} {
   233  	return st.findByIDs(ids, true)
   234  }
   235  
   236  // findByIDs returns a list of elements of the given ids.
   237  func (st *Queue) findByIDs(ids []int, lock bool) (result []interface{}) {
   238  	if lock {
   239  		st.rwmutex.Lock()
   240  		defer st.rwmutex.Unlock()
   241  	}
   242  
   243  	length := len(ids)
   244  	count := 0
   245  
   246  	for id, item := range st.slice {
   247  		for _, i := range ids {
   248  			if id == i {
   249  				result = append(result, item)
   250  				count++
   251  				break
   252  			}
   253  		}
   254  
   255  		if count == length {
   256  			break
   257  		}
   258  	}
   259  
   260  	return
   261  }
   262  
   263  // Remove removes an element from the queue
   264  func (st *Queue) Remove(target interface{}) *ErrMsg {
   265  	st.rwmutex.Lock()
   266  	defer st.rwmutex.Unlock()
   267  
   268  	index := st.findOne(target, false)
   269  
   270  	if index == -1 {
   271  		return NewErrorMessage(ErrorIDNotFound)
   272  	}
   273  
   274  	// remove the element
   275  	st.slice = append(st.slice[:index], st.slice[index+1:]...)
   276  
   277  	return nil
   278  }
   279  
   280  // GetLen returns the number of enqueued elements
   281  func (st *Queue) GetLen() int {
   282  	st.rwmutex.RLock()
   283  	defer st.rwmutex.RUnlock()
   284  
   285  	return len(st.slice)
   286  }
   287  
   288  // GetCap returns the queue's capacity
   289  func (st *Queue) GetCap() int {
   290  	st.rwmutex.RLock()
   291  	defer st.rwmutex.RUnlock()
   292  
   293  	return cap(st.slice)
   294  }
   295  
   296  // Swap swaps values A and B.
   297  func (st *Queue) Swap(a interface{}, b interface{}) *ErrMsg {
   298  	st.rwmutex.Lock()
   299  	defer st.rwmutex.Unlock()
   300  
   301  	length := len(st.slice)
   302  	if length == 0 {
   303  		return NewErrorMessage(ErrorQueueEmpty)
   304  	}
   305  
   306  	aIndex := st.findOne(a, false)
   307  	bIndex := st.findOne(b, false)
   308  
   309  	if aIndex == -1 || bIndex == -1 {
   310  		return NewErrorMessage(ErrorIDNotFound)
   311  	}
   312  
   313  	if aIndex == bIndex {
   314  		return NewErrorMessage(ErrorQueueSwapIndexesMatch)
   315  	}
   316  
   317  	st.slice[aIndex], st.slice[bIndex] = st.slice[bIndex], st.slice[aIndex]
   318  
   319  	return nil
   320  }
   321  
   322  // MoveToFront moves an element to the front of the queue
   323  func (st *Queue) MoveToFront(target interface{}) *ErrMsg {
   324  	st.rwmutex.Lock()
   325  	defer st.rwmutex.Unlock()
   326  
   327  	length := len(st.slice)
   328  	if length == 0 {
   329  		return NewErrorMessage(ErrorQueueEmpty)
   330  	}
   331  
   332  	index := st.findOne(target, false)
   333  	if index == -1 {
   334  		return NewErrorMessage(ErrorIDNotFound)
   335  	}
   336  
   337  	if index == 0 {
   338  		return NewErrorMessage(ErrorQueueMoveIndexFrontPosition)
   339  	}
   340  
   341  	// Moves the element all the way to the back of the queue.
   342  	// The element is moved one position at a time using bubble sort algorithm.
   343  	for i := index; i >= 1; i-- {
   344  		st.slice[i], st.slice[i-1] = st.slice[i-1], st.slice[i]
   345  	}
   346  	return nil
   347  }
   348  
   349  // MoveToBack moves an element to the back of the queue
   350  func (st *Queue) MoveToBack(target interface{}) *ErrMsg {
   351  	st.rwmutex.Lock()
   352  	defer st.rwmutex.Unlock()
   353  
   354  	length := len(st.slice)
   355  	if length == 0 {
   356  		return NewErrorMessage(ErrorQueueEmpty)
   357  	}
   358  
   359  	index := st.findOne(target, false)
   360  
   361  	if index == -1 {
   362  		return NewErrorMessage(ErrorIDNotFound)
   363  	}
   364  
   365  	if index == length-1 {
   366  		return NewErrorMessage(ErrorQueueMoveIndexBackPosition)
   367  	}
   368  
   369  	// Moves the element all the way to the back of the queue.
   370  	// The element is moved one position at a time using bubble sort algorithm.
   371  	for i := index; i < length-1; i++ {
   372  		st.slice[i], st.slice[i+1] = st.slice[i+1], st.slice[i]
   373  	}
   374  
   375  	return nil
   376  }