github.com/tursom/GoCollections@v0.3.10/concurrent/collections/ConcurrentLinkedQueue.go (about)

     1  /*
     2   * Copyright (c) 2022 tursom. All rights reserved.
     3   * Use of this source code is governed by a GPL-3
     4   * license that can be found in the LICENSE file.
     5   */
     6  
     7  package collections
     8  
     9  import (
    10  	"github.com/tursom/GoCollections/collections"
    11  	"github.com/tursom/GoCollections/exceptions"
    12  	"github.com/tursom/GoCollections/lang"
    13  	"github.com/tursom/GoCollections/lang/atomic"
    14  )
    15  
    16  type (
    17  	// ConcurrentLinkedQueue FIFO data struct, impl by linked list.
    18  	// in order to reuse ConcurrentLinkedStack, element will offer on queue's end, and poll on queue's head.
    19  	ConcurrentLinkedQueue[T lang.Object] struct {
    20  		lang.BaseObject
    21  		ConcurrentLinkedStack[T]
    22  		end *linkedStackNode[T]
    23  	}
    24  
    25  	linkedQueueIterator[T lang.Object] struct {
    26  		node  *linkedStackNode[T]
    27  		queue *ConcurrentLinkedQueue[T]
    28  	}
    29  )
    30  
    31  func (q *ConcurrentLinkedQueue[T]) String() string {
    32  	return collections.String[T](q)
    33  }
    34  
    35  func NewLinkedQueue[T lang.Object]() *ConcurrentLinkedQueue[T] {
    36  	return &ConcurrentLinkedQueue[T]{}
    37  }
    38  
    39  func (q *ConcurrentLinkedQueue[T]) Iterator() collections.Iterator[T] {
    40  	return q.MutableIterator()
    41  }
    42  
    43  func (q *ConcurrentLinkedQueue[T]) Offer(element T) exceptions.Exception {
    44  	_, err := q.offerAndGetNode(element)
    45  	return err
    46  }
    47  
    48  func (q *ConcurrentLinkedQueue[T]) OfferAndGetNode(element T) (collections.QueueNode[T], exceptions.Exception) {
    49  	newNode, err := q.offerAndGetNode(element)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	return &linkedQueueIterator[T]{queue: q, node: newNode}, nil
    54  }
    55  
    56  // offerAndGetNode offer an element on q.end
    57  func (q *ConcurrentLinkedQueue[T]) offerAndGetNode(element T) (*linkedStackNode[T], exceptions.Exception) {
    58  	newNode := &linkedStackNode[T]{value: element}
    59  	q.size.Add(1)
    60  
    61  	var next **linkedStackNode[T]
    62  	ref := q.end
    63  
    64  	// bug fix
    65  	// buf caused by delete q.end but not update it's reference
    66  	for ref != nil && ref.deleted {
    67  		ref = ref.next
    68  	}
    69  
    70  	// q.end is nil when queue just created
    71  	switch {
    72  	case ref == nil:
    73  		next = &q.head
    74  	default:
    75  		next = &ref.next
    76  	}
    77  	for !atomic.CompareAndSwapPointer(next, nil, newNode) {
    78  		if ref == nil || ref.next == nil {
    79  			next = &q.head
    80  			ref = q.head
    81  		} else {
    82  			for ref.next != nil {
    83  				ref = ref.next
    84  			}
    85  			next = &ref.next
    86  		}
    87  
    88  		// bug fix
    89  		// q.head may be deleted on async env
    90  		for *next != nil {
    91  			next = &(*next).next
    92  		}
    93  	}
    94  	q.end = newNode
    95  	return newNode, nil
    96  }
    97  
    98  func (q *ConcurrentLinkedQueue[T]) Poll() (T, exceptions.Exception) {
    99  	return q.Pop()
   100  }
   101  
   102  func (q *ConcurrentLinkedQueue[T]) MutableIterator() collections.MutableIterator[T] {
   103  	return &linkedQueueIterator[T]{queue: q, node: q.head}
   104  }
   105  
   106  // Size size of queue
   107  // it may not correct on concurrent environment, to check it's empty, use func IsEmpty
   108  func (q *ConcurrentLinkedQueue[T]) Size() int {
   109  	return int(q.size.Load())
   110  }
   111  
   112  func (q *ConcurrentLinkedQueue[T]) IsEmpty() bool {
   113  	return q.head == nil
   114  }
   115  
   116  func (q *ConcurrentLinkedQueue[T]) Contains(element T) bool {
   117  	return collections.Contains[T](q, element)
   118  }
   119  
   120  func (q *ConcurrentLinkedQueue[T]) ContainsAll(collection collections.Collection[T]) bool {
   121  	return collections.ContainsAll[T](q, collection)
   122  }
   123  
   124  func (q *ConcurrentLinkedQueue[T]) Add(element T) bool {
   125  	exception := q.Push(element)
   126  	exceptions.Print(exception)
   127  	return exception == nil
   128  }
   129  
   130  func (q *ConcurrentLinkedQueue[T]) Remove(element T) exceptions.Exception {
   131  	return collections.Remove[T](q, element)
   132  }
   133  
   134  func (q *ConcurrentLinkedQueue[T]) AddAll(collection collections.Collection[T]) bool {
   135  	return collections.AddAll[T](q, collection)
   136  }
   137  
   138  func (q *ConcurrentLinkedQueue[T]) RemoveAll(collection collections.Collection[T]) bool {
   139  	return collections.RemoveAll[T](q, collection)
   140  }
   141  
   142  func (q *ConcurrentLinkedQueue[T]) RetainAll(collection collections.Collection[T]) bool {
   143  	return collections.RetainAll[T](q, collection)
   144  }
   145  
   146  func (q *ConcurrentLinkedQueue[T]) Clear() {
   147  	q.head = nil
   148  	q.end = nil
   149  	q.size.Store(0)
   150  }
   151  
   152  func (i *linkedQueueIterator[T]) HasNext() bool {
   153  	for i.node != nil && i.node.deleted {
   154  		i.node = i.node.next
   155  	}
   156  	return i.node != nil
   157  }
   158  
   159  func (i *linkedQueueIterator[T]) Next() (T, exceptions.Exception) {
   160  	value, err := i.Get()
   161  	i.node = i.node.next
   162  	for i.node != nil && i.node.deleted {
   163  		i.node = i.node.next
   164  	}
   165  	return value, err
   166  }
   167  
   168  func (i *linkedQueueIterator[T]) Get() (T, exceptions.Exception) {
   169  	return i.node.value, nil
   170  }
   171  
   172  func (i *linkedQueueIterator[T]) Set(value T) exceptions.Exception {
   173  	i.node.value = value
   174  	return nil
   175  }
   176  
   177  func (i *linkedQueueIterator[T]) Remove() exceptions.Exception {
   178  	_, err := i.RemoveAndGet()
   179  	return err
   180  }
   181  
   182  func (i *linkedQueueIterator[T]) RemoveAndGet() (T, exceptions.Exception) {
   183  	if i.node == nil {
   184  		return lang.Nil[T](), nil
   185  	}
   186  	load := i.node
   187  	load.deleted = true
   188  	i.queue.size.Add(-1)
   189  	i.queue.deleted.Add(1)
   190  	i.queue.CleanDeleted()
   191  	i.node = load.next
   192  	return load.value, nil
   193  }