Skip to content

Commit 258f637

Browse files
committed
runtime/netpoll: revise for go1.12
Update #3
1 parent aa474c5 commit 258f637

File tree

4 files changed

+101
-74
lines changed

4 files changed

+101
-74
lines changed

gosrc/runtime/netpoll.go

Lines changed: 87 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
5+
// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
66

77
package runtime
88

@@ -56,9 +56,11 @@ type pollDesc struct {
5656
lock mutex // protects the following fields
5757
fd uintptr
5858
closing bool
59-
seq uintptr // protects from stale timers and ready notifications
59+
user uint32 // user settable cookie
60+
rseq uintptr // protects from stale read timers
6061
rg uintptr // pdReady, pdWait, G waiting for read or nil
6162
rt timer // read deadline timer (set if rt.f != nil)
63+
wseq uintptr // protects from stale write timers
6264
rd int64 // read deadline
6365
wg uintptr // pdReady, pdWait, G waiting for write or nil
6466
wt timer // write deadline timer
@@ -92,12 +94,19 @@ func netpollinited() bool {
9294
return atomic.Load(&netpollInited) != 0
9395
}
9496

95-
//go:linkname poll_runtime_pollServerDescriptor internal/poll.runtime_pollServerDescriptor
97+
//go:linkname poll_runtime_isPollServerDescriptor internal/poll.runtime_isPollServerDescriptor
9698

97-
// poll_runtime_pollServerDescriptor returns the descriptor being used,
98-
// or ^uintptr(0) if the system does not use a poll descriptor.
99-
func poll_runtime_pollServerDescriptor() uintptr {
100-
return netpolldescriptor()
99+
// poll_runtime_isPollServerDescriptor reports whether fd is a
100+
// descriptor being used by netpoll.
101+
func poll_runtime_isPollServerDescriptor(fd uintptr) bool {
102+
fds := netpolldescriptor()
103+
if GOOS != "aix" {
104+
return fd == fds
105+
} else {
106+
// AIX have a pipe in its netpoll implementation.
107+
// Therefore, two fd are returned by netpolldescriptor using a mask.
108+
return fd == fds&0xFFFF || fd == (fds>>16)&0xFFFF
109+
}
101110
}
102111

103112
//go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen
@@ -112,9 +121,10 @@ func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
112121
}
113122
pd.fd = fd
114123
pd.closing = false
115-
pd.seq++
124+
pd.rseq++
116125
pd.rg = 0
117126
pd.rd = 0
127+
pd.wseq++
118128
pd.wg = 0
119129
pd.wd = 0
120130
unlock(&pd.lock)
@@ -166,8 +176,8 @@ func poll_runtime_pollWait(pd *pollDesc, mode int) int {
166176
if err != 0 {
167177
return err
168178
}
169-
// As for now only Solaris uses level-triggered IO.
170-
if GOOS == "solaris" {
179+
// As for now only Solaris and AIX use level-triggered IO.
180+
if GOOS == "solaris" || GOOS == "aix" {
171181
netpollarm(pd, mode)
172182
}
173183
for !netpollblock(pd, int32(mode), false) {
@@ -197,59 +207,73 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
197207
unlock(&pd.lock)
198208
return
199209
}
200-
pd.seq++ // invalidate current timers
201-
// Reset current timers.
202-
if pd.rt.f != nil {
203-
deltimer(&pd.rt)
204-
pd.rt.f = nil
205-
}
206-
if pd.wt.f != nil {
207-
deltimer(&pd.wt)
208-
pd.wt.f = nil
209-
}
210-
// Setup new timers.
211-
if d != 0 && d <= nanotime() {
212-
d = -1
210+
rd0, wd0 := pd.rd, pd.wd
211+
combo0 := rd0 > 0 && rd0 == wd0
212+
if d > 0 {
213+
d += nanotime()
214+
if d <= 0 {
215+
// If the user has a deadline in the future, but the delay calculation
216+
// overflows, then set the deadline to the maximum possible value.
217+
d = 1<<63 - 1
218+
}
213219
}
214220
if mode == 'r' || mode == 'r'+'w' {
215221
pd.rd = d
216222
}
217223
if mode == 'w' || mode == 'r'+'w' {
218224
pd.wd = d
219225
}
220-
if pd.rd > 0 && pd.rd == pd.wd {
221-
pd.rt.f = netpollDeadline
222-
pd.rt.when = pd.rd
223-
// Copy current seq into the timer arg.
224-
// Timer func will check the seq against current descriptor seq,
225-
// if they differ the descriptor was reused or timers were reset.
226-
pd.rt.arg = pd
227-
pd.rt.seq = pd.seq
228-
addtimer(&pd.rt)
229-
} else {
226+
combo := pd.rd > 0 && pd.rd == pd.wd
227+
rtf := netpollReadDeadline
228+
if combo {
229+
rtf = netpollDeadline
230+
}
231+
if pd.rt.f == nil {
230232
if pd.rd > 0 {
231-
pd.rt.f = netpollReadDeadline
233+
pd.rt.f = rtf
232234
pd.rt.when = pd.rd
235+
// Copy current seq into the timer arg.
236+
// Timer func will check the seq against current descriptor seq,
237+
// if they differ the descriptor was reused or timers were reset.
233238
pd.rt.arg = pd
234-
pd.rt.seq = pd.seq
239+
pd.rt.seq = pd.rseq
235240
addtimer(&pd.rt)
236241
}
237-
if pd.wd > 0 {
242+
} else if pd.rd != rd0 || combo != combo0 {
243+
pd.rseq++ // invalidate current timers
244+
if pd.rd > 0 {
245+
modtimer(&pd.rt, pd.rd, 0, rtf, pd, pd.rseq)
246+
} else {
247+
deltimer(&pd.rt)
248+
pd.rt.f = nil
249+
}
250+
}
251+
if pd.wt.f == nil {
252+
if pd.wd > 0 && !combo {
238253
pd.wt.f = netpollWriteDeadline
239254
pd.wt.when = pd.wd
240255
pd.wt.arg = pd
241-
pd.wt.seq = pd.seq
256+
pd.wt.seq = pd.wseq
242257
addtimer(&pd.wt)
243258
}
259+
} else if pd.wd != wd0 || combo != combo0 {
260+
pd.wseq++ // invalidate current timers
261+
if pd.wd > 0 && !combo {
262+
modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd, pd.wseq)
263+
} else {
264+
deltimer(&pd.wt)
265+
pd.wt.f = nil
266+
}
244267
}
245268
// If we set the new deadline in the past, unblock currently pending IO if any.
246-
var rg, wg *g
247-
atomicstorep(unsafe.Pointer(&wg), nil) // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock
248-
if pd.rd < 0 {
249-
rg = netpollunblock(pd, 'r', false)
250-
}
251-
if pd.wd < 0 {
252-
wg = netpollunblock(pd, 'w', false)
269+
if pd.rd < 0 || pd.wd < 0 {
270+
atomic.StorepNoWB(noescape(unsafe.Pointer(&wg)), nil) // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock
271+
if pd.rd < 0 {
272+
rg = netpollunblock(pd, 'r', false)
273+
}
274+
if pd.wd < 0 {
275+
wg = netpollunblock(pd, 'w', false)
276+
}
253277
}
254278
unlock(&pd.lock)
255279
if rg != nil {
@@ -267,9 +291,10 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
267291
throw("runtime: unblock on closing polldesc")
268292
}
269293
pd.closing = true
270-
pd.seq++
294+
pd.rseq++
295+
pd.wseq++
271296
var rg, wg *g
272-
atomicstorep(unsafe.Pointer(&rg), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
297+
atomic.StorepNoWB(noescape(unsafe.Pointer(&rg)), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock
273298
rg = netpollunblock(pd, 'r', false)
274299
wg = netpollunblock(pd, 'w', false)
275300
if pd.rt.f != nil {
@@ -289,24 +314,22 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
289314
}
290315
}
291316

292-
// make pd ready, newly runnable goroutines (if any) are returned in rg/wg
317+
// make pd ready, newly runnable goroutines (if any) are added to toRun.
293318
// May run during STW, so write barriers are not allowed.
294319
//go:nowritebarrier
295-
func netpollready(gpp *guintptr, pd *pollDesc, mode int32) {
296-
var rg, wg guintptr
320+
func netpollready(toRun *gList, pd *pollDesc, mode int32) {
321+
var rg, wg *g
297322
if mode == 'r' || mode == 'r'+'w' {
298-
rg.set(netpollunblock(pd, 'r', true))
323+
rg = netpollunblock(pd, 'r', true)
299324
}
300325
if mode == 'w' || mode == 'r'+'w' {
301-
wg.set(netpollunblock(pd, 'w', true))
326+
wg = netpollunblock(pd, 'w', true)
302327
}
303-
if rg != 0 {
304-
rg.ptr().schedlink = *gpp
305-
*gpp = rg
328+
if rg != nil {
329+
toRun.push(rg)
306330
}
307-
if wg != 0 {
308-
wg.ptr().schedlink = *gpp
309-
*gpp = wg
331+
if wg != nil {
332+
toRun.push(wg)
310333
}
311334
}
312335

@@ -406,7 +429,11 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
406429
lock(&pd.lock)
407430
// Seq arg is seq when the timer was set.
408431
// If it's stale, ignore the timer event.
409-
if seq != pd.seq {
432+
currentSeq := pd.rseq
433+
if !read {
434+
currentSeq = pd.wseq
435+
}
436+
if seq != currentSeq {
410437
// The descriptor was reused or timers were reset.
411438
unlock(&pd.lock)
412439
return
@@ -417,7 +444,7 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
417444
throw("runtime: inconsistent read deadline")
418445
}
419446
pd.rd = -1
420-
atomicstorep(unsafe.Pointer(&pd.rt.f), nil) // full memory barrier between store to rd and load of rg in netpollunblock
447+
atomic.StorepNoWB(unsafe.Pointer(&pd.rt.f), nil) // full memory barrier between store to rd and load of rg in netpollunblock
421448
rg = netpollunblock(pd, 'r', false)
422449
}
423450
var wg *g
@@ -426,7 +453,7 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
426453
throw("runtime: inconsistent write deadline")
427454
}
428455
pd.wd = -1
429-
atomicstorep(unsafe.Pointer(&pd.wt.f), nil) // full memory barrier between store to wd and load of wg in netpollunblock
456+
atomic.StorepNoWB(unsafe.Pointer(&pd.wt.f), nil) // full memory barrier between store to wd and load of wg in netpollunblock
430457
wg = netpollunblock(pd, 'w', false)
431458
}
432459
unlock(&pd.lock)

gosrc/runtime/netpoll_epoll.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ func netpollarm(pd *pollDesc, mode int) {
5858

5959
// polls for ready network connections
6060
// returns list of goroutines that become runnable
61-
func netpoll(block bool) *g {
61+
func netpoll(block bool) gList {
6262
if epfd == -1 {
63-
return nil
63+
return gList{}
6464
}
6565
waitms := int32(-1)
6666
if !block {
@@ -76,7 +76,7 @@ retry:
7676
}
7777
goto retry
7878
}
79-
var gp guintptr
79+
var toRun gList
8080
for i := int32(0); i < n; i++ {
8181
ev := &events[i]
8282
if ev.events == 0 {
@@ -92,11 +92,11 @@ retry:
9292
if mode != 0 {
9393
pd := *(**pollDesc)(unsafe.Pointer(&ev.data))
9494

95-
netpollready(&gp, pd, mode)
95+
netpollready(&toRun, pd, mode)
9696
}
9797
}
98-
if block && gp == 0 {
98+
if block && toRun.empty() {
9999
goto retry
100100
}
101-
return gp.ptr()
101+
return toRun
102102
}

gosrc/runtime/netpoll_fake.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ func netpollclose(fd uintptr) int32 {
2727
func netpollarm(pd *pollDesc, mode int) {
2828
}
2929

30-
func netpoll(block bool) *g {
31-
return nil
30+
func netpoll(block bool) gList {
31+
return gList
3232
}

gosrc/runtime/netpoll_kqueue.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ func netpollarm(pd *pollDesc, mode int) {
5959

6060
// Polls for ready network connections.
6161
// Returns list of goroutines that become runnable.
62-
func netpoll(block bool) *g {
62+
func netpoll(block bool) gList {
6363
if kq == -1 {
64-
return nil
64+
return gList{}
6565
}
6666
var tp *timespec
6767
var ts timespec
@@ -78,7 +78,7 @@ retry:
7878
}
7979
goto retry
8080
}
81-
var gp guintptr
81+
var toRun gList
8282
for i := 0; i < int(n); i++ {
8383
ev := &events[i]
8484
var mode int32
@@ -102,11 +102,11 @@ retry:
102102
mode += 'w'
103103
}
104104
if mode != 0 {
105-
netpollready(&gp, (*pollDesc)(unsafe.Pointer(ev.udata)), mode)
105+
netpollready(&toRun, (*pollDesc)(unsafe.Pointer(ev.udata)), mode)
106106
}
107107
}
108-
if block && gp == 0 {
108+
if block && toRun.empty() {
109109
goto retry
110110
}
111-
return gp.ptr()
111+
return toRun
112112
}

0 commit comments

Comments
 (0)