]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: define lock order between G status and channel lock
authorAustin Clements <austin@google.com>
Sun, 21 Feb 2016 15:40:39 +0000 (10:40 -0500)
committerAustin Clements <austin@google.com>
Wed, 16 Mar 2016 20:13:17 +0000 (20:13 +0000)
Currently, locking a G's stack by setting its status to _Gcopystack or
_Gscan is unordered with respect to channel locks. However, when we
make stack shrinking concurrent, stack shrinking will need to lock the
G and then acquire channel locks, which imposes an order on these.

Document this lock ordering and fix closechan to respect it.
Everything else already happens to respect it.

For #12967.

Change-Id: I4dd02675efffb3e7daa5285cf75bf24f987d90d4
Reviewed-on: https://go-review.googlesource.com/20041
Reviewed-by: Rick Hudson <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/runtime/chan.go

index e89831783e93f11657e8f228399e90d9647f9cda..954b389f47cff05858c73105098fdcf1eb5fcced 100644 (file)
@@ -36,6 +36,10 @@ type hchan struct {
 
        // lock protects all fields in hchan, as well as several
        // fields in sudogs blocked on this channel.
+       //
+       // Do not change another G's status while holding this lock
+       // (in particular, do not ready a G), as this can deadlock
+       // with stack shrinking.
        lock mutex
 }
 
@@ -315,6 +319,8 @@ func closechan(c *hchan) {
 
        c.closed = 1
 
+       var glist *g
+
        // release all readers
        for {
                sg := c.recvq.dequeue()
@@ -333,7 +339,8 @@ func closechan(c *hchan) {
                if raceenabled {
                        raceacquireg(gp, unsafe.Pointer(c))
                }
-               goready(gp, 3)
+               gp.schedlink.set(glist)
+               glist = gp
        }
 
        // release all writers (they will panic)
@@ -351,9 +358,18 @@ func closechan(c *hchan) {
                if raceenabled {
                        raceacquireg(gp, unsafe.Pointer(c))
                }
-               goready(gp, 3)
+               gp.schedlink.set(glist)
+               glist = gp
        }
        unlock(&c.lock)
+
+       // Ready all Gs now that we've dropped the channel lock.
+       for glist != nil {
+               gp := glist
+               glist = glist.schedlink.ptr()
+               gp.schedlink = 0
+               goready(gp, 3)
+       }
 }
 
 // entry points for <- c from compiled code