Exported on 9/17/2025 at 11:26:10 GMT+2 from Cursor (1.6.26)
User
Does this systemstack call lead to a call that might transition a goroutine that is blocked on networking to runnable state? Follow the functions inside of it and see if they call into the scheduler/netpoll thing to make that happen.
systemstack(func() {
// Mark our goroutine preemptible so its stack
// can be scanned or observed by the execution
// tracer. This, for example, lets two mark workers
// scan each other (otherwise, they would
// deadlock). We must not modify anything on
// the G stack. However, stack shrinking is
// disabled for mark workers, so it is safe to
// read from the G stack.
//
// N.B. The execution tracer is not aware of this status
// transition and handles it specially based on the
// wait reason.
casGToWaitingForSuspendG(gp, _Grunning, waitReasonGCWorkerActive)
switch pp.gcMarkWorkerMode {
default:
throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")
case gcMarkWorkerDedicatedMode:
gcDrainMarkWorkerDedicated(&pp.gcw, true)
if gp.preempt {
// We were preempted. This is
// a useful signal to kick
// everything out of the run
// queue so it can run
// somewhere else.
if drainQ := runqdrain(pp); !drainQ.empty() {
lock(&sched.lock)
globrunqputbatch(&drainQ)
unlock(&sched.lock)
}
}
// Go back to draining, this time
// without preemption.
gcDrainMarkWorkerDedicated(&pp.gcw, false)
case gcMarkWorkerFractionalMode:
gcDrainMarkWorkerFractional(&pp.gcw)
case gcMarkWorkerIdleMode:
gcDrainMarkWorkerIdle(&pp.gcw)
}
casgstatus(gp, _Gwaiting, _Grunning)
})Cursor
I'll help you trace through the functions called within this systemstack block to see if any of them could transition a goroutine blocked on networking to a runnable state. Let me start by examining the gc drain functions that are called.
Now let me look at the gcDrain function implementation to see what it calls:
Let me search for the pollWork function that's called in idle mode to see if it leads to netpoll:
func pollWork() bool {
if !sched.runq.empty() {
return true
}
p := getg().m.p.ptr()
if !runqempty(p) {
return true
}
if netpollinited() && netpollAnyWaiters() && sched.lastpoll.Load() != 0 {
if list, delta := netpoll(0); !list.empty() {
injectglist(&list)
netpollAdjustWaiters(delta)
return true
}
}
return false
}Yes, this systemstack call can indeed lead to transitioning goroutines blocked on networking to a runnable state, but only in idle mode.
Here's the call chain:
- When
pp.gcMarkWorkerMode == gcMarkWorkerIdleMode, it callsgcDrainMarkWorkerIdle() gcDrainMarkWorkerIdlecallsgcDrainwith thegcDrainIdleflag- With the
gcDrainIdleflag,gcDrainsetscheck = pollWork - During the drain loop, it periodically calls
pollWork()to check if there's other work to do
Looking at the pollWork function:
func pollWork() bool {
// ... check run queues ...
if netpollinited() && netpollAnyWaiters() && sched.lastpoll.Load() != 0 {
if list, delta := netpoll(0); !list.empty() {
injectglist(&list)
netpollAdjustWaiters(delta)
return true
}
}
return false
}The key part is that pollWork calls netpoll(0) - a non-blocking network poll that:
- Checks for any goroutines blocked on network I/O that are now ready
- Returns a list of such goroutines
- Calls
injectglist(&list)to inject them back into the scheduler's run queue, making them runnable
So yes, when the GC mark worker is running in idle mode, it will periodically check netpoll and wake up any goroutines that were blocked on network I/O and are now ready to run. This makes sense because idle workers should yield when there's other work available, including network I/O that has become ready.
The dedicated and fractional modes do not perform this netpoll check - they focus solely on GC marking work.
func pollWork() bool {
// ... check run queues ...
if netpollinited() && netpollAnyWaiters() && sched.lastpoll.Load() != 0 {
if list, delta := netpoll(0); !list.empty() {
injectglist(&list)
netpollAdjustWaiters(delta)
return true
}
}
return false
}User
Create a small markdown file called gcBgMarkWorker_wakeup.md where you explain how the above. Include links into the code on GitHub for go version go1.25.0. Also include small code snippets where needed. Keep it simple and short.
Cursor
I'll create a concise markdown file explaining how gcBgMarkWorker can wake up goroutines blocked on networking.
see gcBgMarkWorker_wakeup.mdI've created the markdown file gcBgMarkWorker_wakeup.md that explains how the gcBgMarkWorker can wake up goroutines blocked on networking. The file includes:
- A clear explanation that this only happens in idle mode
- The call chain from
gcBgMarkWorker→gcDrainMarkWorkerIdle→gcDrain→pollWork - Key code snippets showing the relevant parts
- GitHub links to the exact lines in Go version 1.25.0
- A summary of why this design makes sense (idle workers should yield when there's other work)
The document is concise and focuses on the essential mechanism of how network-blocked goroutines can be transitioned to runnable state during GC marking in idle mode.