Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,228 @@
|
||||
// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// Go-style goroutine runtime for V.
|
||||
// Implements the GMP (Goroutine-Machine-Processor) scheduling model
|
||||
// translated from the Go runtime (src/runtime/proc.go, runtime2.go).
|
||||
//
|
||||
// Goroutine - goroutine: lightweight unit of execution with its own stack
|
||||
// Machine - machine: OS thread that executes goroutines
|
||||
// Processor - processor: logical processor that manages a local run queue
|
||||
//
|
||||
// Design doc: https://golang.org/s/go11sched
|
||||
@[has_globals]
|
||||
module goroutines
|
||||
|
||||
import sync
|
||||
import runtime as _
|
||||
|
||||
// GoFn is the type for a goroutine function: takes a voidptr argument.
|
||||
pub type GoFn = fn (voidptr)
|
||||
|
||||
// Default goroutine stack size.
|
||||
// Go uses 8KB with growable stacks; V/C needs more since stacks are fixed.
|
||||
const default_stack_size = 256 * 1024
|
||||
|
||||
// Maximum number of goroutines in a Processor's local run queue (matches Go's 256)
|
||||
const local_queue_size = 256
|
||||
|
||||
// How often to check the global queue for fairness (matches Go's 61)
|
||||
const global_queue_check_interval = 61
|
||||
|
||||
// GStatus represents goroutine states (matches Go's runtime2.go)
|
||||
pub enum GStatus {
|
||||
idle // just allocated, not yet initialized
|
||||
runnable // on a run queue, not currently executing
|
||||
running // executing user code on an Machine
|
||||
waiting // blocked (channel, mutex, etc.)
|
||||
dead // finished execution, available for reuse
|
||||
copystack // stack is being moved (not used yet)
|
||||
}
|
||||
|
||||
// PStatus represents processor states
|
||||
pub enum PStatus {
|
||||
idle // not being used, available on idle list
|
||||
running // owned by an Machine and executing code
|
||||
stopped // halted
|
||||
dead // no longer used
|
||||
}
|
||||
|
||||
// Goroutine represents a goroutine - the fundamental unit of concurrent execution.
|
||||
// Translated from Go's `type g struct` in runtime2.go.
|
||||
pub struct Goroutine {
|
||||
pub mut:
|
||||
id u64 // unique goroutine id
|
||||
status GStatus = .idle // current state
|
||||
stack voidptr // stack memory (allocated)
|
||||
stack_size int = default_stack_size // stack allocation size
|
||||
context Context // saved CPU context for switching (ucontext_t or similar)
|
||||
fn_ptr voidptr // function to execute
|
||||
fn_arg voidptr // argument to the function
|
||||
sched_link &Goroutine = unsafe { nil } // linked list link for run queues
|
||||
m &Machine = unsafe { nil } // current Machine executing this Goroutine (nil if not running)
|
||||
parent_id u64 // goroutine id of creator
|
||||
wait_reason string // if status==waiting, why
|
||||
preempt bool // preemption signal
|
||||
}
|
||||
|
||||
// Machine represents a machine (OS thread) that executes goroutines.
|
||||
// Translated from Go's `type m struct` in runtime2.go.
|
||||
pub struct Machine {
|
||||
pub mut:
|
||||
id i64
|
||||
g0 &Goroutine = unsafe { nil } // goroutine with scheduling stack
|
||||
curg &Goroutine = unsafe { nil } // current running goroutine
|
||||
p &Processor = unsafe { nil } // attached processor (nil if not executing Go code)
|
||||
spinning bool // looking for work
|
||||
blocked bool // blocked on a note
|
||||
park sync.Semaphore // for parking/unparking
|
||||
sched_link &Machine = unsafe { nil } // linked list for idle Machine list
|
||||
thread thread // underlying OS thread handle
|
||||
}
|
||||
|
||||
// Processor represents a processor - a resource required to execute goroutines.
|
||||
// Each Processor has a local run queue. Translated from Go's `type p struct` in runtime2.go.
|
||||
pub struct Processor {
|
||||
pub mut:
|
||||
id i32
|
||||
status PStatus = .idle
|
||||
m &Machine = unsafe { nil } // back-link to associated Machine
|
||||
sched_tick u32 // incremented on every scheduler call
|
||||
|
||||
// Local run queue - lock-free SPMC ring buffer (matches Go's design)
|
||||
runq_head u32 // consumer index (atomic)
|
||||
runq_tail u32 // producer index (atomic)
|
||||
runq [local_queue_size]&Goroutine // circular buffer
|
||||
runnext &Goroutine = unsafe { nil } // next Goroutine to run (fast path, like Go's runnext)
|
||||
|
||||
// Free Goroutine list for reuse
|
||||
g_free GoroutineList
|
||||
|
||||
// ID cache to avoid contention on the global counter
|
||||
goid_cache u64
|
||||
goid_cache_end u64
|
||||
|
||||
link &Processor = unsafe { nil } // linked list for idle Processor list
|
||||
}
|
||||
|
||||
// Sched is the global scheduler state.
|
||||
// Translated from Go's `type schedt struct` in runtime2.go.
|
||||
pub struct Sched {
|
||||
pub mut:
|
||||
goid_gen u64 // global goroutine ID generator (atomic)
|
||||
|
||||
mu SpinLock // protects idle lists, global queue (spinlock is ucontext-safe)
|
||||
|
||||
// Idle Machine's waiting for work
|
||||
midle &Machine = unsafe { nil }
|
||||
nmidle i32
|
||||
|
||||
// Idle Processor's
|
||||
pidle &Processor = unsafe { nil }
|
||||
npidle i32
|
||||
|
||||
// Global run queue
|
||||
runq GoroutineQueue
|
||||
nmspinning i32 // number of spinning Machine's (atomic)
|
||||
|
||||
// All Processor's (indexed by id)
|
||||
allp []&Processor
|
||||
|
||||
// Total Machine count
|
||||
mnext i64
|
||||
maxmcount i32 = 10000
|
||||
|
||||
// Global Goroutine free list
|
||||
g_free_mu SpinLock
|
||||
g_free GoroutineList
|
||||
g_free_count i32
|
||||
|
||||
// Shutdown
|
||||
stopped bool
|
||||
}
|
||||
|
||||
// GoroutineQueue is a simple linked-list queue of Goroutine's (matches Go's gQueue).
|
||||
pub struct GoroutineQueue {
|
||||
pub mut:
|
||||
head &Goroutine = unsafe { nil }
|
||||
tail &Goroutine = unsafe { nil }
|
||||
size i32
|
||||
}
|
||||
|
||||
// GoroutineList is a list of Goroutine's.
|
||||
pub struct GoroutineList {
|
||||
pub mut:
|
||||
head &Goroutine = unsafe { nil }
|
||||
count i32
|
||||
}
|
||||
|
||||
fn (mut q GoroutineQueue) push_back(gp &Goroutine) {
|
||||
mut g := unsafe { gp }
|
||||
g.sched_link = unsafe { nil }
|
||||
if q.tail != unsafe { nil } {
|
||||
q.tail.sched_link = g
|
||||
} else {
|
||||
q.head = g
|
||||
}
|
||||
q.tail = g
|
||||
q.size++
|
||||
}
|
||||
|
||||
fn (mut q GoroutineQueue) push(gp &Goroutine) {
|
||||
mut g := unsafe { gp }
|
||||
g.sched_link = q.head
|
||||
q.head = g
|
||||
if q.tail == unsafe { nil } {
|
||||
q.tail = g
|
||||
}
|
||||
q.size++
|
||||
}
|
||||
|
||||
fn (mut q GoroutineQueue) pop() &Goroutine {
|
||||
if q.head == unsafe { nil } {
|
||||
return unsafe { nil }
|
||||
}
|
||||
gp := q.head
|
||||
q.head = gp.sched_link
|
||||
if q.head == unsafe { nil } {
|
||||
q.tail = unsafe { nil }
|
||||
}
|
||||
q.size--
|
||||
return gp
|
||||
}
|
||||
|
||||
fn (q &GoroutineQueue) empty() bool {
|
||||
return q.head == unsafe { nil }
|
||||
}
|
||||
|
||||
fn (mut l GoroutineList) push(gp &Goroutine) {
|
||||
mut g := unsafe { gp }
|
||||
g.sched_link = l.head
|
||||
l.head = g
|
||||
l.count++
|
||||
}
|
||||
|
||||
fn (mut l GoroutineList) pop() &Goroutine {
|
||||
if l.head == unsafe { nil } {
|
||||
return unsafe { nil }
|
||||
}
|
||||
gp := l.head
|
||||
l.head = gp.sched_link
|
||||
l.count--
|
||||
return gp
|
||||
}
|
||||
|
||||
fn (l &GoroutineList) empty() bool {
|
||||
return l.head == unsafe { nil }
|
||||
}
|
||||
|
||||
// Global scheduler instance
|
||||
__global gsched = Sched{}
|
||||
|
||||
// Number of processors (defaults to number of CPU cores)
|
||||
__global gomaxprocs = i32(0)
|
||||
|
||||
// All goroutines ever created (for debugging)
|
||||
__global allgs_mu = SpinLock{}
|
||||
__global allgs = []&Goroutine{}
|
||||
Reference in new issue
Block a user