wantPanic: "close of synctest channel from outside bubble",
}} {
t.Run(test.desc, func(t *testing.T) {
- donec := make(chan struct{})
- ch := make(chan chan struct{})
- go func() {
- defer close(donec)
- defer wantPanic(t, test.wantPanic)
- test.f(<-ch)
- }()
- synctest.Run(func() {
- ch <- make(chan struct{})
+ // Bubbled channel accessed from outside any bubble.
+ t.Run("outside_bubble", func(t *testing.T) {
+ donec := make(chan struct{})
+ ch := make(chan chan struct{})
+ go func() {
+ defer close(donec)
+ defer wantPanic(t, test.wantPanic)
+ test.f(<-ch)
+ }()
+ synctest.Run(func() {
+ ch <- make(chan struct{})
+ })
+ <-donec
+ })
+ // Bubbled channel accessed from a different bubble.
+ t.Run("different_bubble", func(t *testing.T) {
+ donec := make(chan struct{})
+ ch := make(chan chan struct{})
+ go func() {
+ defer close(donec)
+ c := <-ch
+ synctest.Run(func() {
+ defer wantPanic(t, test.wantPanic)
+ test.f(c)
+ })
+ }()
+ synctest.Run(func() {
+ ch <- make(chan struct{})
+ })
+ <-donec
})
- <-donec
})
}
}
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
- synctest bool // true if created in a synctest bubble
closed uint32
timer *timer // timer feeding this chan
elemtype *_type // element type
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
+ bubble *synctestBubble
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
c.elemsize = uint16(elem.Size_)
c.elemtype = elem
c.dataqsiz = uint(size)
- if getg().bubble != nil {
- c.synctest = true
+ if b := getg().bubble; b != nil {
+ c.bubble = b
}
lockInit(&c.lock, lockRankHchan)
racereadpc(c.raceaddr(), callerpc, abi.FuncPCABIInternal(chansend))
}
- if c.synctest && getg().bubble == nil {
+ if c.bubble != nil && getg().bubble != c.bubble {
panic(plainError("send on synctest channel from outside bubble"))
}
// stack shrinking.
gp.parkingOnChan.Store(true)
reason := waitReasonChanSend
- if c.synctest {
+ if c.bubble != nil {
reason = waitReasonSynctestChanSend
}
gopark(chanparkcommit, unsafe.Pointer(&c.lock), reason, traceBlockChanSend, 2)
// sg must already be dequeued from c.
// ep must be non-nil and point to the heap or the caller's stack.
func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
- if c.synctest && sg.g.bubble != getg().bubble {
+ if c.bubble != nil && getg().bubble != c.bubble {
unlockf()
panic(plainError("send on synctest channel from outside bubble"))
}
if c == nil {
panic(plainError("close of nil channel"))
}
- if c.synctest && getg().bubble == nil {
+ if c.bubble != nil && getg().bubble != c.bubble {
panic(plainError("close of synctest channel from outside bubble"))
}
throw("unreachable")
}
- if c.synctest && getg().bubble == nil {
+ if c.bubble != nil && getg().bubble != c.bubble {
panic(plainError("receive on synctest channel from outside bubble"))
}
// stack shrinking.
gp.parkingOnChan.Store(true)
reason := waitReasonChanReceive
- if c.synctest {
+ if c.bubble != nil {
reason = waitReasonSynctestChanReceive
}
gopark(chanparkcommit, unsafe.Pointer(&c.lock), reason, traceBlockChanRecv, 2)
// sg must already be dequeued from c.
// A non-nil ep must point to the heap or the caller's stack.
func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
- if c.synctest && sg.g.bubble != getg().bubble {
+ if c.bubble != nil && getg().bubble != c.bubble {
unlockf()
panic(plainError("receive on synctest channel from outside bubble"))
}