Skip to content

Commit 70b1baa

Browse files
committed
fix(linked): make linked queue implementation thread-safe
1 parent a043348 commit 70b1baa

File tree

2 files changed

+78
-39
lines changed

2 files changed

+78
-39
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
---
1717

18-
The `queue` package provides thread-safe generic implementations in Go for the following data structures: `BlockingQueue`, `PriorityQueue` and `CircularQueue`.
18+
The `queue` package provides thread-safe generic implementations in Go for the following data structures: `BlockingQueue`, `PriorityQueue`, `CircularQueue` and `Linked Queue`.
1919

2020
A queue is a sequence of entities that is open at both ends where the elements are
2121
added (enqueued) to the tail (back) of the queue and removed (dequeued) from the head (front) of the queue.

linked.go

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package queue
22

3+
import (
4+
"sync"
5+
)
6+
37
var _ Queue[any] = (*Linked[any])(nil)
48

59
// node is an individual element of the linked list.
@@ -10,13 +14,14 @@ type node[T any] struct {
1014

1115
// Linked represents a data structure representing a queue that uses a
1216
// linked list for its internal storage.
13-
// ! The Linked Queue is not thread safe.
1417
type Linked[T comparable] struct {
1518
head *node[T] // first node of the queue.
1619
tail *node[T] // last node of the queue.
1720
size int // number of elements in the queue.
1821
// nolint: revive
1922
initialElements []T // initial elements with which the queue was created, allowing for a reset to its original state if needed.
23+
// synchronization
24+
lock sync.RWMutex
2025
}
2126

2227
// NewLinked creates a new Linked containing the given elements.
@@ -31,59 +36,76 @@ func NewLinked[T comparable](elements []T) *Linked[T] {
3136
copy(queue.initialElements, elements)
3237

3338
for _, element := range elements {
34-
_ = queue.Offer(element)
39+
_ = queue.offer(element)
3540
}
3641

3742
return queue
3843
}
3944

4045
// Get retrieves and removes the head of the queue.
41-
func (q *Linked[T]) Get() (elem T, _ error) {
42-
if q.IsEmpty() {
46+
func (lq *Linked[T]) Get() (elem T, _ error) {
47+
lq.lock.Lock()
48+
defer lq.lock.Unlock()
49+
50+
if lq.isEmpty() {
4351
return elem, ErrNoElementsAvailable
4452
}
4553

46-
value := q.head.value
47-
q.head = q.head.next
48-
q.size--
54+
value := lq.head.value
55+
lq.head = lq.head.next
56+
lq.size--
4957

50-
if q.IsEmpty() {
51-
q.tail = nil
58+
if lq.isEmpty() {
59+
lq.tail = nil
5260
}
5361

5462
return value, nil
5563
}
5664

5765
// Offer inserts the element into the queue.
58-
func (q *Linked[T]) Offer(value T) error {
66+
func (lq *Linked[T]) Offer(value T) error {
67+
lq.lock.Lock()
68+
defer lq.lock.Unlock()
69+
70+
return lq.offer(value)
71+
}
72+
73+
// offer inserts the element into the queue.
74+
func (lq *Linked[T]) offer(value T) error {
5975
newNode := &node[T]{value: value}
6076

61-
if q.IsEmpty() {
62-
q.head = newNode
77+
if lq.isEmpty() {
78+
lq.head = newNode
6379
} else {
64-
q.tail.next = newNode
80+
lq.tail.next = newNode
6581
}
6682

67-
q.tail = newNode
68-
q.size++
83+
lq.tail = newNode
84+
lq.size++
6985

7086
return nil
7187
}
7288

7389
// Reset sets the queue to its initial state.
74-
func (q *Linked[T]) Reset() {
75-
q.head = nil
76-
q.tail = nil
77-
q.size = 0
90+
func (lq *Linked[T]) Reset() {
91+
lq.lock.Lock()
92+
defer lq.lock.Unlock()
93+
94+
lq.head = nil
95+
lq.tail = nil
96+
lq.size = 0
7897

79-
for _, element := range q.initialElements {
80-
_ = q.Offer(element)
98+
for _, element := range lq.initialElements {
99+
_ = lq.offer(element)
81100
}
82101
}
83102

84103
// Contains returns true if the queue contains the element.
85-
func (q *Linked[T]) Contains(value T) bool {
86-
current := q.head
104+
func (lq *Linked[T]) Contains(value T) bool {
105+
lq.lock.RLock()
106+
defer lq.lock.RUnlock()
107+
108+
current := lq.head
87109
for current != nil {
88110
if current.value == value {
89111
return true
@@ -96,30 +118,44 @@ func (q *Linked[T]) Contains(value T) bool {
96118
}
97119

98120
// Peek retrieves but does not remove the head of the queue.
99-
func (q *Linked[T]) Peek() (elem T, _ error) {
100-
if q.IsEmpty() {
121+
func (lq *Linked[T]) Peek() (elem T, _ error) {
122+
lq.lock.RLock()
123+
defer lq.lock.RUnlock()
124+
125+
if lq.isEmpty() {
101126
return elem, ErrNoElementsAvailable
102127
}
103128

104-
return q.head.value, nil
129+
return lq.head.value, nil
105130
}
106131

107132
// Size returns the number of elements in the queue.
108-
func (q *Linked[T]) Size() int {
109-
return q.size
133+
func (lq *Linked[T]) Size() int {
134+
lq.lock.RLock()
135+
defer lq.lock.RUnlock()
136+
137+
return lq.size
110138
}
111139

112140
// IsEmpty returns true if the queue is empty, false otherwise.
113-
func (q *Linked[T]) IsEmpty() bool {
114-
return q.size == 0
141+
func (lq *Linked[T]) IsEmpty() bool {
142+
lq.lock.RLock()
143+
defer lq.lock.RUnlock()
144+
145+
return lq.isEmpty()
146+
}
147+
148+
// IsEmpty returns true if the queue is empty, false otherwise.
149+
func (lq *Linked[T]) isEmpty() bool {
150+
return lq.size == 0
115151
}
116152

117153
// Iterator returns a channel that will be filled with the elements.
118154
// It removes the elements from the queue.
119-
func (q *Linked[T]) Iterator() <-chan T {
155+
func (lq *Linked[T]) Iterator() <-chan T {
120156
ch := make(chan T)
121157

122-
elems := q.Clear()
158+
elems := lq.Clear()
123159

124160
go func() {
125161
for _, e := range elems {
@@ -133,20 +169,23 @@ func (q *Linked[T]) Iterator() <-chan T {
133169
}
134170

135171
// Clear removes and returns all elements from the queue.
136-
func (q *Linked[T]) Clear() []T {
137-
elements := make([]T, 0, q.size)
172+
func (lq *Linked[T]) Clear() []T {
173+
lq.lock.Lock()
174+
defer lq.lock.Unlock()
175+
176+
elements := make([]T, 0, lq.size)
138177

139-
current := q.head
178+
current := lq.head
140179
for current != nil {
141180
elements = append(elements, current.value)
142181
next := current.next
143182
current = next
144183
}
145184

146185
// Clear the queue
147-
q.head = nil
148-
q.tail = nil
149-
q.size = 0
186+
lq.head = nil
187+
lq.tail = nil
188+
lq.size = 0
150189

151190
return elements
152191
}

0 commit comments

Comments
 (0)