Skip to content

goroutines #

goroutines

Go-style goroutine runtime for V, implementing the GMP (Goroutine-Machine-Processor) scheduling model translated from the Go runtime (src/runtime/proc.go, runtime2.go, chan.go).

Overview

This module provides lightweight goroutines for V's go keyword, as opposed to spawn which creates OS threads.

GMP Model

  • G (Goroutine): Lightweight unit of execution with its own stack (~8KB default)
  • M (Machine): OS thread that executes goroutines
  • P (Processor): Logical processor with a local run queue (one per CPU core)

Key Features

  • Work stealing: Idle processors steal work from busy ones
  • Local run queues: Lock-free per-P queues minimize contention
  • Global run queue: Overflow and fairness mechanism
  • Goroutine parking: Efficient blocking/unblocking for channels
  • G reuse: Dead goroutines are recycled to reduce allocation

Usage

// `go` launches a goroutine (lightweight, scheduled by GMP)
go expensive_computation()

// `spawn` launches an OS thread (traditional V behavior)
spawn blocking_io_task()

Architecture

Translated from Go's runtime source:

Go Source V Module File Purpose
runtime2.go goroutines.v Core data structures (G, M, P, Sched)
proc.go scheduler.v Scheduler loop, work stealing, run queues
proc.go park.v gopark/goready, Sudog, WaitQ
proc.go init.v Initialization (schedinit, procresize)
chan.go chan.v Channel implementation
asm (gogo/gosave) context_nix.c.v Context switching (ucontext)
asm (gogo/gosave) context_windows.c.v Context switching (Windows fibers)

References

fn chan_cap #

fn chan_cap(c &Chan) int

chan_cap returns the capacity of the channel buffer.

fn chan_close #

fn chan_close(c &Chan)

chan_close closes the channel. Translated from Go's closechan() in chan.go.

fn chan_len #

fn chan_len(c &Chan) int

chan_len returns the number of elements in the channel buffer.

fn chan_make #

fn chan_make(elem_size int, buf_size int) &Chan

chan_make creates a new channel. If buf_size > 0, creates a buffered channel. Translated from Go's makechan() in chan.go.

fn chan_recv #

fn chan_recv(c &Chan, ep voidptr, block bool) (bool, bool)

chan_recv receives a value from the channel. If block is true, blocks until a value is available. Returns (received, ok). ok is false if channel is closed and empty. Translated from Go's chanrecv() in chan.go.

fn chan_send #

fn chan_send(c &Chan, ep voidptr, block bool) bool

chan_send sends a value on the channel. If block is true, blocks until the send can proceed. Returns true if the value was sent. Translated from Go's chansend() in chan.go.

fn context_init #

fn context_init(mut ctx Context, stack voidptr, stack_size int, entry_fn fn (voidptr), arg voidptr)

context_init initializes a context for a new goroutine. Sets up the context to run entry_fn with arg on the given stack.

fn context_set #

fn context_set(to &Context)

context_set switches to the given context without saving.

fn context_switch #

fn context_switch(mut from Context, to &Context)

context_switch saves the current context into from and switches to to.

fn get_current_g #

fn get_current_g() &Goroutine

get_current_g returns the currently running G.

fn gopark #

fn gopark(reason string)

gopark puts the current goroutine into a waiting state. The goroutine can be made runnable again by calling goready. reason describes why the goroutine is parking (for debugging). Translated from Go's gopark() in proc.go.

fn goready #

fn goready(gp &Goroutine)

goready puts a waiting goroutine back on a run queue. Translated from Go's goready() in proc.go.

fn goroutine_create #

fn goroutine_create(f voidptr, arg voidptr, arg_size int)

goroutine_create creates a new goroutine to run f with argument arg. This is the equivalent of Go's newproc function. Called by the compiler for go expr().

fn gosched #

fn gosched()

gosched yields the processor, allowing other goroutines to run. Translated from Go's Gosched() / goschedImpl() in proc.go.

fn num_goroutine #

fn num_goroutine() int

num_goroutine returns the number of goroutines that currently exist.

fn schedule #

fn schedule()

schedule is the main scheduler entry point. Finds a runnable goroutine and executes it. Never returns. Translated from Go's schedule() in proc.go.

fn set_max_procs #

fn set_max_procs(n int) int

set_max_procs changes the number of active processors. Returns the previous value. Translated from Go's GOMAXPROCS().

fn shutdown #

fn shutdown()

shutdown gracefully shuts down the goroutine scheduler.

type GoFn #

type GoFn = fn (voidptr)

GoFn is the type for a goroutine function: takes a voidptr argument.

enum GStatus #

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)
}

GStatus represents goroutine states (matches Go's runtime2.go)

enum PStatus #

enum PStatus {
	idle    // not being used, available on idle list
	running // owned by an Machine and executing code
	stopped // halted
	dead    // no longer used
}

PStatus represents processor states

struct Chan #

struct Chan {
pub mut:
	mu       SpinLock // protects all fields (spinlock is ucontext-safe)
	qcount   u32      // total data in the queue
	dataqsiz u32      // size of the circular buffer
	buf      voidptr  // circular buffer for buffered channels
	elemsize u16      // size of each element
	closed   bool     // true if channel is closed

	sendx u32 // send index into circular buffer
	recvx u32 // receive index into circular buffer

	recvq WaitQ // list of recv waiters
	sendq WaitQ // list of send waiters
}

Chan is a goroutine-safe channel for communication between goroutines. Translated from Go's hchan struct in chan.go.

struct Context #

struct Context {
pub mut:
	uctx C.ucontext_t
}

Context wraps ucontext_t for goroutine context switching.

struct Goroutine #

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
}

Goroutine represents a goroutine - the fundamental unit of concurrent execution. Translated from Go's type g struct in runtime2.go.

struct GoroutineList #

struct GoroutineList {
pub mut:
	head  &Goroutine = unsafe { nil }
	count i32
}

GoroutineList is a list of Goroutine's.

struct GoroutineQueue #

struct GoroutineQueue {
pub mut:
	head &Goroutine = unsafe { nil }
	tail &Goroutine = unsafe { nil }
	size i32
}

GoroutineQueue is a simple linked-list queue of Goroutine's (matches Go's gQueue).

struct Machine #

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
}

Machine represents a machine (OS thread) that executes goroutines. Translated from Go's type m struct in runtime2.go.

struct Processor #

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
}

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.

struct Sched #

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
}

Sched is the global scheduler state. Translated from Go's type schedt struct in runtime2.go.

struct SpinLock #

struct SpinLock {
mut:
	state i32
}

SpinLock - ucontext-safe lock (pthreads mutex breaks with swapcontext).

fn (SpinLock) acquire #

fn (mut s SpinLock) acquire()

fn (SpinLock) release #

fn (mut s SpinLock) release()

struct Sudog #

struct Sudog {
pub mut:
	g       &Goroutine = unsafe { nil } // the waiting goroutine
	next    &Sudog     = unsafe { nil } // next in wait list
	prev    &Sudog     = unsafe { nil } // prev in wait list
	elem    voidptr // data element (may point to stack)
	success bool    // true if woken by successful channel op
	c       voidptr // channel pointer
}

Sudog represents a G in a wait list (e.g., channel wait queue). Translated from Go's sudog struct in runtime2.go.

struct WaitQ #

struct WaitQ {
pub mut:
	first &Sudog = unsafe { nil }
	last  &Sudog = unsafe { nil }
}

WaitQ is a wait queue of Sudogs (used by channels). Translated from Go's waitq struct in runtime2.go.

fn (WaitQ) enqueue #

fn (mut q WaitQ) enqueue(s &Sudog)

fn (WaitQ) dequeue #

fn (mut q WaitQ) dequeue() &Sudog

fn (WaitQ) empty #

fn (q &WaitQ) empty() bool