diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-02-07 10:49:33 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-02-07 23:43:36 +0000 |
commit | 60d437f99468906935f35e5c6fbd31c7228a1045 (patch) | |
tree | 529978bbbf8eaff168b9c02648a2882fcd2dc025 | |
parent | b8061825e57dcee97a37fe49272ca52e84600f5e (diff) | |
download | go-60d437f99468906935f35e5c6fbd31c7228a1045.tar.gz go-60d437f99468906935f35e5c6fbd31c7228a1045.zip |
runtime: avoid double notewakeup in netpoll stub code
Otherwise we can see
- goroutine 1 calls netpollBreak, the atomic.Cas succeeds, then suspends
- goroutine 2 calls noteclear, sets netpollBroken to 0
- goroutine 3 calls netpollBreak, the atomic.Cas succeeds, calls notewakeup
- goroutine 1 wakes up calls notewakeup, crashes due to double wakeup
This doesn't happen on Plan 9 because it only runs one thread at a time.
But Fuschia wants to use this code too.
Change-Id: Ib636e4f327bb15e44a2c40fd681aae9a91073a30
Reviewed-on: https://go-review.googlesource.com/c/go/+/218537
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
-rw-r--r-- | src/runtime/netpoll_stub.go | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/src/runtime/netpoll_stub.go b/src/runtime/netpoll_stub.go index fe45cfbd40..f86f2f6174 100644 --- a/src/runtime/netpoll_stub.go +++ b/src/runtime/netpoll_stub.go @@ -13,16 +13,23 @@ var netpollWaiters uint32 var netpollStubLock mutex var netpollNote note -var netpollBroken uint32 + +// netpollBroken, protected by netpollBrokenLock, avoids a double notewakeup. +var netpollBrokenLock mutex +var netpollBroken bool func netpollGenericInit() { atomic.Store(&netpollInited, 1) } func netpollBreak() { - if atomic.Cas(&netpollBroken, 0, 1) { + lock(&netpollBrokenLock) + broken := netpollBroken + netpollBroken = true + if !broken { notewakeup(&netpollNote) } + unlock(&netpollBrokenLock) } // Polls for ready network connections. @@ -34,8 +41,12 @@ func netpoll(delay int64) gList { // This lock ensures that only one goroutine tries to use // the note. It should normally be completely uncontended. lock(&netpollStubLock) + + lock(&netpollBrokenLock) noteclear(&netpollNote) - atomic.Store(&netpollBroken, 0) + netpollBroken = false + unlock(&netpollBrokenLock) + notetsleep(&netpollNote, delay) unlock(&netpollStubLock) } |