
While adding some code to clear the Next field when popping from a task stack for safety reasons, the clear was placed outside of a nil pointer check. As a result, (*internal/task.Stack).Pop panicked when the Stack is empty.
98 строки
1,9 КиБ
Go
98 строки
1,9 КиБ
Go
package task
|
|
|
|
const asserts = false
|
|
|
|
// Queue is a FIFO container of tasks.
|
|
// The zero value is an empty queue.
|
|
type Queue struct {
|
|
head, tail *Task
|
|
}
|
|
|
|
// Push a task onto the queue.
|
|
func (q *Queue) Push(t *Task) {
|
|
if asserts && t.Next != nil {
|
|
panic("runtime: pushing a task to a queue with a non-nil Next pointer")
|
|
}
|
|
if q.tail != nil {
|
|
q.tail.Next = t
|
|
}
|
|
q.tail = t
|
|
t.Next = nil
|
|
if q.head == nil {
|
|
q.head = t
|
|
}
|
|
}
|
|
|
|
// Pop a task off of the queue.
|
|
func (q *Queue) Pop() *Task {
|
|
t := q.head
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
q.head = t.Next
|
|
if q.tail == t {
|
|
q.tail = nil
|
|
}
|
|
t.Next = nil
|
|
return t
|
|
}
|
|
|
|
// Append pops the contents of another queue and pushes them onto the end of this queue.
|
|
func (q *Queue) Append(other *Queue) {
|
|
if q.head == nil {
|
|
q.head = other.head
|
|
} else {
|
|
q.tail.Next = other.head
|
|
}
|
|
q.tail = other.tail
|
|
other.head, other.tail = nil, nil
|
|
}
|
|
|
|
// Stack is a LIFO container of tasks.
|
|
// The zero value is an empty stack.
|
|
// This is slightly cheaper than a queue, so it can be preferable when strict ordering is not necessary.
|
|
type Stack struct {
|
|
top *Task
|
|
}
|
|
|
|
// Push a task onto the stack.
|
|
func (s *Stack) Push(t *Task) {
|
|
if asserts && t.Next != nil {
|
|
panic("runtime: pushing a task to a stack with a non-nil Next pointer")
|
|
}
|
|
s.top, t.Next = t, s.top
|
|
}
|
|
|
|
// Pop a task off of the stack.
|
|
func (s *Stack) Pop() *Task {
|
|
t := s.top
|
|
if t != nil {
|
|
s.top = t.Next
|
|
t.Next = nil
|
|
}
|
|
return t
|
|
}
|
|
|
|
// tail follows the chain of tasks.
|
|
// If t is nil, returns nil.
|
|
// Otherwise, returns the task in the chain where the Next field is nil.
|
|
func (t *Task) tail() *Task {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
for t.Next != nil {
|
|
t = t.Next
|
|
}
|
|
return t
|
|
}
|
|
|
|
// Queue moves the contents of the stack into a queue.
|
|
// Elements can be popped from the queue in the same order that they would be popped from the stack.
|
|
func (s *Stack) Queue() Queue {
|
|
head := s.top
|
|
s.top = nil
|
|
return Queue{
|
|
head: head,
|
|
tail: head.tail(),
|
|
}
|
|
}
|