package dsUtils import ( "io" "sync" ) type IQueue[T any] interface { Push(item T) Get() T Len() int } // Queue isn't thread safe and suppose to be used only in a single thread // For multithreaded usage -- use QueueSync type Queue[T any] struct { IQueue[T] Buffer []T } // NewQueue creates a new Queue instance func NewQueue[T any]() *Queue[T] { return &Queue[T]{} } func (q *Queue[T]) Push(item T) { q.Buffer = append(q.Buffer, item) } // Get returns a single item from a queue. If queue is empty -- it will return io.EOF func (q *Queue[T]) Get() (T, error) { if len(q.Buffer) == 0 { var out T return out, io.EOF } item := q.Buffer[0] q.Buffer = q.Buffer[1:] return item, nil } func (q *Queue[T]) Len() int { return len(q.Buffer) } // QueueSync is a Queue but for multi thread usage type QueueSync[T any] struct { IQueue[T] Buffer []T Mu *sync.Mutex NonEmptyCond *sync.Cond } // NewQueueSync creates a new QueueSync instance func NewQueueSync[T any]() *QueueSync[T] { return &QueueSync[T]{ Mu: &sync.Mutex{}, NonEmptyCond: sync.NewCond(&sync.RWMutex{}), } } func (q *QueueSync[T]) Push(item T) { q.Mu.Lock() q.Buffer = append(q.Buffer, item) q.NonEmptyCond.Signal() q.Mu.Unlock() } func (q *QueueSync[T]) Get() T { q.Mu.Lock() if len(q.Buffer) < 1 { q.NonEmptyCond.Wait() } item := q.Buffer[0] q.Buffer = q.Buffer[1:] q.Mu.Unlock() return item } func (q *QueueSync[T]) Len() int { q.Mu.Lock() defer q.Mu.Lock() return len(q.Buffer) }