mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
16fc1ea2bb
Match the Go runtime by terminating for deadlocks, stack overflows, runtime and GC invariants, invalid lock operations, and platform initialization failures instead of routing them through panic/recover. Keep language-level runtime errors and unsupported user operations recoverable. Add crash coverage that verifies fatal errors bypass deferred recover calls.
138 lines
3.0 KiB
Go
138 lines
3.0 KiB
Go
package task
|
|
|
|
import "runtime/interrupt"
|
|
|
|
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) {
|
|
mask := lockAtomics()
|
|
if asserts && t.Next != nil {
|
|
unlockAtomics(mask)
|
|
runtimeFatal("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
|
|
}
|
|
unlockAtomics(mask)
|
|
}
|
|
|
|
// Pop a task off of the queue.
|
|
func (q *Queue) Pop() *Task {
|
|
mask := lockAtomics()
|
|
t := q.head
|
|
if t == nil {
|
|
unlockAtomics(mask)
|
|
return nil
|
|
}
|
|
q.head = t.Next
|
|
if q.tail == t {
|
|
q.tail = nil
|
|
}
|
|
t.Next = nil
|
|
unlockAtomics(mask)
|
|
return t
|
|
}
|
|
|
|
// Append pops the contents of another queue and pushes them onto the end of this queue.
|
|
func (q *Queue) Append(other *Queue) {
|
|
mask := lockAtomics()
|
|
if q.head == nil {
|
|
q.head = other.head
|
|
} else {
|
|
q.tail.Next = other.head
|
|
}
|
|
q.tail = other.tail
|
|
other.head, other.tail = nil, nil
|
|
unlockAtomics(mask)
|
|
}
|
|
|
|
// Empty checks if the queue is empty.
|
|
func (q *Queue) Empty() bool {
|
|
mask := lockAtomics()
|
|
empty := q.head == nil
|
|
unlockAtomics(mask)
|
|
return empty
|
|
}
|
|
|
|
// 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) {
|
|
mask := lockAtomics()
|
|
if asserts && t.Next != nil {
|
|
unlockAtomics(mask)
|
|
runtimeFatal("runtime: pushing a task to a stack with a non-nil Next pointer")
|
|
}
|
|
s.top, t.Next = t, s.top
|
|
unlockAtomics(mask)
|
|
}
|
|
|
|
// Pop a task off of the stack.
|
|
func (s *Stack) Pop() *Task {
|
|
mask := lockAtomics()
|
|
t := s.top
|
|
if t != nil {
|
|
s.top = t.Next
|
|
t.Next = nil
|
|
}
|
|
unlockAtomics(mask)
|
|
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 {
|
|
mask := lockAtomics()
|
|
head := s.top
|
|
s.top = nil
|
|
q := Queue{
|
|
head: head,
|
|
tail: head.tail(),
|
|
}
|
|
unlockAtomics(mask)
|
|
return q
|
|
}
|
|
|
|
// Use runtime.lockAtomics and runtime.unlockAtomics so that Queue and Stack
|
|
// work correctly even on multicore systems. These functions are normally used
|
|
// to implement atomic operations, but the same spinlock can also be used for
|
|
// Queue/Stack operations which are very fast.
|
|
// These functions are just plain old interrupt disable/restore on non-multicore
|
|
// systems.
|
|
|
|
//go:linkname lockAtomics runtime.lockAtomics
|
|
func lockAtomics() interrupt.State
|
|
|
|
//go:linkname unlockAtomics runtime.unlockAtomics
|
|
func unlockAtomics(mask interrupt.State)
|