Commit de990545 authored by Austin Clements's avatar Austin Clements

runtime: use gList in netpoll

netpoll is perhaps one of the most confusing uses of G lists currently
since it passes around many lists as bare *g values right now.
Switching to gList makes it much clearer what's an individual g and
what's a list.

Change-Id: I8d8993c4967c5bae049c7a094aad3a657928ba6c
Reviewed-on: https://go-review.googlesource.com/129397
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRick Hudson <rlh@golang.org>
parent 3578918b
...@@ -289,24 +289,22 @@ func poll_runtime_pollUnblock(pd *pollDesc) { ...@@ -289,24 +289,22 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
} }
} }
// make pd ready, newly runnable goroutines (if any) are returned in rg/wg // make pd ready, newly runnable goroutines (if any) are added to toRun.
// May run during STW, so write barriers are not allowed. // May run during STW, so write barriers are not allowed.
//go:nowritebarrier //go:nowritebarrier
func netpollready(gpp *guintptr, pd *pollDesc, mode int32) { func netpollready(toRun *gList, pd *pollDesc, mode int32) {
var rg, wg guintptr var rg, wg *g
if mode == 'r' || mode == 'r'+'w' { if mode == 'r' || mode == 'r'+'w' {
rg.set(netpollunblock(pd, 'r', true)) rg = netpollunblock(pd, 'r', true)
} }
if mode == 'w' || mode == 'r'+'w' { if mode == 'w' || mode == 'r'+'w' {
wg.set(netpollunblock(pd, 'w', true)) wg = netpollunblock(pd, 'w', true)
} }
if rg != 0 { if rg != nil {
rg.ptr().schedlink = *gpp toRun.push(rg)
*gpp = rg
} }
if wg != 0 { if wg != nil {
wg.ptr().schedlink = *gpp toRun.push(wg)
*gpp = wg
} }
} }
......
...@@ -58,9 +58,9 @@ func netpollarm(pd *pollDesc, mode int) { ...@@ -58,9 +58,9 @@ func netpollarm(pd *pollDesc, mode int) {
// polls for ready network connections // polls for ready network connections
// returns list of goroutines that become runnable // returns list of goroutines that become runnable
func netpoll(block bool) *g { func netpoll(block bool) gList {
if epfd == -1 { if epfd == -1 {
return nil return gList{}
} }
waitms := int32(-1) waitms := int32(-1)
if !block { if !block {
...@@ -76,7 +76,7 @@ retry: ...@@ -76,7 +76,7 @@ retry:
} }
goto retry goto retry
} }
var gp guintptr var toRun gList
for i := int32(0); i < n; i++ { for i := int32(0); i < n; i++ {
ev := &events[i] ev := &events[i]
if ev.events == 0 { if ev.events == 0 {
...@@ -92,11 +92,11 @@ retry: ...@@ -92,11 +92,11 @@ retry:
if mode != 0 { if mode != 0 {
pd := *(**pollDesc)(unsafe.Pointer(&ev.data)) pd := *(**pollDesc)(unsafe.Pointer(&ev.data))
netpollready(&gp, pd, mode) netpollready(&toRun, pd, mode)
} }
} }
if block && gp == 0 { if block && toRun.empty() {
goto retry goto retry
} }
return gp.ptr() return toRun
} }
...@@ -27,6 +27,6 @@ func netpollclose(fd uintptr) int32 { ...@@ -27,6 +27,6 @@ func netpollclose(fd uintptr) int32 {
func netpollarm(pd *pollDesc, mode int) { func netpollarm(pd *pollDesc, mode int) {
} }
func netpoll(block bool) *g { func netpoll(block bool) gList {
return nil return gList{}
} }
...@@ -59,9 +59,9 @@ func netpollarm(pd *pollDesc, mode int) { ...@@ -59,9 +59,9 @@ func netpollarm(pd *pollDesc, mode int) {
// Polls for ready network connections. // Polls for ready network connections.
// Returns list of goroutines that become runnable. // Returns list of goroutines that become runnable.
func netpoll(block bool) *g { func netpoll(block bool) gList {
if kq == -1 { if kq == -1 {
return nil return gList{}
} }
var tp *timespec var tp *timespec
var ts timespec var ts timespec
...@@ -78,7 +78,7 @@ retry: ...@@ -78,7 +78,7 @@ retry:
} }
goto retry goto retry
} }
var gp guintptr var toRun gList
for i := 0; i < int(n); i++ { for i := 0; i < int(n); i++ {
ev := &events[i] ev := &events[i]
var mode int32 var mode int32
...@@ -102,11 +102,11 @@ retry: ...@@ -102,11 +102,11 @@ retry:
mode += 'w' mode += 'w'
} }
if mode != 0 { if mode != 0 {
netpollready(&gp, (*pollDesc)(unsafe.Pointer(ev.udata)), mode) netpollready(&toRun, (*pollDesc)(unsafe.Pointer(ev.udata)), mode)
} }
} }
if block && gp == 0 { if block && toRun.empty() {
goto retry goto retry
} }
return gp.ptr() return toRun
} }
...@@ -180,9 +180,9 @@ func netpollarm(pd *pollDesc, mode int) { ...@@ -180,9 +180,9 @@ func netpollarm(pd *pollDesc, mode int) {
// polls for ready network connections // polls for ready network connections
// returns list of goroutines that become runnable // returns list of goroutines that become runnable
func netpoll(block bool) *g { func netpoll(block bool) gList {
if portfd == -1 { if portfd == -1 {
return nil return gList{}
} }
var wait *timespec var wait *timespec
...@@ -202,7 +202,7 @@ retry: ...@@ -202,7 +202,7 @@ retry:
goto retry goto retry
} }
var gp guintptr var toRun gList
for i := 0; i < int(n); i++ { for i := 0; i < int(n); i++ {
ev := &events[i] ev := &events[i]
...@@ -233,12 +233,12 @@ retry: ...@@ -233,12 +233,12 @@ retry:
} }
if mode != 0 { if mode != 0 {
netpollready(&gp, pd, mode) netpollready(&toRun, pd, mode)
} }
} }
if block && gp == 0 { if block && toRun.empty() {
goto retry goto retry
} }
return gp.ptr() return toRun
} }
...@@ -10,10 +10,10 @@ var netpollWaiters uint32 ...@@ -10,10 +10,10 @@ var netpollWaiters uint32
// Polls for ready network connections. // Polls for ready network connections.
// Returns list of goroutines that become runnable. // Returns list of goroutines that become runnable.
func netpoll(block bool) (gp *g) { func netpoll(block bool) gList {
// Implementation for platforms that do not support // Implementation for platforms that do not support
// integrated network poller. // integrated network poller.
return return gList{}
} }
func netpollinited() bool { func netpollinited() bool {
......
...@@ -63,17 +63,17 @@ func netpollarm(pd *pollDesc, mode int) { ...@@ -63,17 +63,17 @@ func netpollarm(pd *pollDesc, mode int) {
// Polls for completed network IO. // Polls for completed network IO.
// Returns list of goroutines that become runnable. // Returns list of goroutines that become runnable.
func netpoll(block bool) *g { func netpoll(block bool) gList {
var entries [64]overlappedEntry var entries [64]overlappedEntry
var wait, qty, key, flags, n, i uint32 var wait, qty, key, flags, n, i uint32
var errno int32 var errno int32
var op *net_op var op *net_op
var gp guintptr var toRun gList
mp := getg().m mp := getg().m
if iocphandle == _INVALID_HANDLE_VALUE { if iocphandle == _INVALID_HANDLE_VALUE {
return nil return gList{}
} }
wait = 0 wait = 0
if block { if block {
...@@ -92,7 +92,7 @@ retry: ...@@ -92,7 +92,7 @@ retry:
mp.blocked = false mp.blocked = false
errno = int32(getlasterror()) errno = int32(getlasterror())
if !block && errno == _WAIT_TIMEOUT { if !block && errno == _WAIT_TIMEOUT {
return nil return gList{}
} }
println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")") println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
throw("runtime: netpoll failed") throw("runtime: netpoll failed")
...@@ -105,7 +105,7 @@ retry: ...@@ -105,7 +105,7 @@ retry:
if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 { if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
errno = int32(getlasterror()) errno = int32(getlasterror())
} }
handlecompletion(&gp, op, errno, qty) handlecompletion(&toRun, op, errno, qty)
} }
} else { } else {
op = nil op = nil
...@@ -118,7 +118,7 @@ retry: ...@@ -118,7 +118,7 @@ retry:
mp.blocked = false mp.blocked = false
errno = int32(getlasterror()) errno = int32(getlasterror())
if !block && errno == _WAIT_TIMEOUT { if !block && errno == _WAIT_TIMEOUT {
return nil return gList{}
} }
if op == nil { if op == nil {
println("runtime: GetQueuedCompletionStatus failed (errno=", errno, ")") println("runtime: GetQueuedCompletionStatus failed (errno=", errno, ")")
...@@ -127,15 +127,15 @@ retry: ...@@ -127,15 +127,15 @@ retry:
// dequeued failed IO packet, so report that // dequeued failed IO packet, so report that
} }
mp.blocked = false mp.blocked = false
handlecompletion(&gp, op, errno, qty) handlecompletion(&toRun, op, errno, qty)
} }
if block && gp == 0 { if block && toRun.empty() {
goto retry goto retry
} }
return gp.ptr() return toRun
} }
func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) { func handlecompletion(toRun *gList, op *net_op, errno int32, qty uint32) {
if op == nil { if op == nil {
println("runtime: GetQueuedCompletionStatus returned op == nil") println("runtime: GetQueuedCompletionStatus returned op == nil")
throw("runtime: netpoll failed") throw("runtime: netpoll failed")
...@@ -147,5 +147,5 @@ func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) { ...@@ -147,5 +147,5 @@ func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) {
} }
op.errno = errno op.errno = errno
op.qty = qty op.qty = qty
netpollready(gpp, op.pd, mode) netpollready(toRun, op.pd, mode)
} }
...@@ -1148,8 +1148,8 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 { ...@@ -1148,8 +1148,8 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 {
_g_.m.locks++ // disable preemption because it can be holding p in a local var _g_.m.locks++ // disable preemption because it can be holding p in a local var
if netpollinited() { if netpollinited() {
gp := netpoll(false) // non-blocking list := netpoll(false) // non-blocking
injectglist(gp) injectglist(list.head.ptr())
} }
add := needaddgcproc() add := needaddgcproc()
lock(&sched.lock) lock(&sched.lock)
...@@ -2312,9 +2312,9 @@ top: ...@@ -2312,9 +2312,9 @@ top:
// not set lastpoll yet), this thread will do blocking netpoll below // not set lastpoll yet), this thread will do blocking netpoll below
// anyway. // anyway.
if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Load64(&sched.lastpoll) != 0 { if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Load64(&sched.lastpoll) != 0 {
if gp := netpoll(false); gp != nil { // non-blocking if list := netpoll(false); !list.empty() { // non-blocking
// netpoll returns list of goroutines linked by schedlink. gp := list.pop()
injectglist(gp.schedlink.ptr()) injectglist(list.head.ptr())
casgstatus(gp, _Gwaiting, _Grunnable) casgstatus(gp, _Gwaiting, _Grunnable)
if trace.enabled { if trace.enabled {
traceGoUnpark(gp, 0) traceGoUnpark(gp, 0)
...@@ -2466,22 +2466,23 @@ stop: ...@@ -2466,22 +2466,23 @@ stop:
if _g_.m.spinning { if _g_.m.spinning {
throw("findrunnable: netpoll with spinning") throw("findrunnable: netpoll with spinning")
} }
gp := netpoll(true) // block until new work is available list := netpoll(true) // block until new work is available
atomic.Store64(&sched.lastpoll, uint64(nanotime())) atomic.Store64(&sched.lastpoll, uint64(nanotime()))
if gp != nil { if !list.empty() {
lock(&sched.lock) lock(&sched.lock)
_p_ = pidleget() _p_ = pidleget()
unlock(&sched.lock) unlock(&sched.lock)
if _p_ != nil { if _p_ != nil {
acquirep(_p_) acquirep(_p_)
injectglist(gp.schedlink.ptr()) gp := list.pop()
injectglist(list.head.ptr())
casgstatus(gp, _Gwaiting, _Grunnable) casgstatus(gp, _Gwaiting, _Grunnable)
if trace.enabled { if trace.enabled {
traceGoUnpark(gp, 0) traceGoUnpark(gp, 0)
} }
return gp, false return gp, false
} }
injectglist(gp) injectglist(list.head.ptr())
} }
} }
stopm() stopm()
...@@ -2501,8 +2502,8 @@ func pollWork() bool { ...@@ -2501,8 +2502,8 @@ func pollWork() bool {
return true return true
} }
if netpollinited() && atomic.Load(&netpollWaiters) > 0 && sched.lastpoll != 0 { if netpollinited() && atomic.Load(&netpollWaiters) > 0 && sched.lastpoll != 0 {
if gp := netpoll(false); gp != nil { if list := netpoll(false); !list.empty() {
injectglist(gp) injectglist(list.head.ptr())
return true return true
} }
} }
...@@ -4387,8 +4388,8 @@ func sysmon() { ...@@ -4387,8 +4388,8 @@ func sysmon() {
now := nanotime() now := nanotime()
if netpollinited() && lastpoll != 0 && lastpoll+10*1000*1000 < now { if netpollinited() && lastpoll != 0 && lastpoll+10*1000*1000 < now {
atomic.Cas64(&sched.lastpoll, uint64(lastpoll), uint64(now)) atomic.Cas64(&sched.lastpoll, uint64(lastpoll), uint64(now))
gp := netpoll(false) // non-blocking - returns list of goroutines list := netpoll(false) // non-blocking - returns list of goroutines
if gp != nil { if !list.empty() {
// Need to decrement number of idle locked M's // Need to decrement number of idle locked M's
// (pretending that one more is running) before injectglist. // (pretending that one more is running) before injectglist.
// Otherwise it can lead to the following situation: // Otherwise it can lead to the following situation:
...@@ -4397,7 +4398,7 @@ func sysmon() { ...@@ -4397,7 +4398,7 @@ func sysmon() {
// observes that there is no work to do and no other running M's // observes that there is no work to do and no other running M's
// and reports deadlock. // and reports deadlock.
incidlelocked(-1) incidlelocked(-1)
injectglist(gp) injectglist(list.head.ptr())
incidlelocked(1) incidlelocked(1)
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment