funcchansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr)bool { // 检查 Channel 是否为空 if c == nil { // 非阻塞直接返回 false if !block { returnfalse } // 向一个空的 Channel 发送数据就会永远阻塞该 Goroutine gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) throw("unreachable") }
if debugChan { print("chansend: chan=", c, "\n") }
if raceenabled { racereadpc(c.raceaddr(), callerpc, funcPC(chansend)) }
// 下面这段注释主要解释了为何不进行加锁就可以判断非阻塞操作是否可以立即返回(不太理解就不翻译了) // Fast path: check for failed non-blocking operation without acquiring the lock. // // After observing that the channel is not closed, we observe that the channel is // not ready for sending. Each of these observations is a single word-sized read // (first c.closed and second c.recvq.first or c.qcount depending on kind of channel). // Because a closed channel cannot transition from 'ready for sending' to // 'not ready for sending', even if the channel is closed between the two observations, // they imply a moment between the two when the channel was both not yet closed // and not ready for sending. We behave as if we observed the channel at that moment, // and report that the send cannot proceed. // // It is okay if the reads are reordered here: if we observe that the channel is not // ready for sending and then observe that it is not closed, that implies that the // channel wasn't closed during the first observation. // 如果(buffer 大小为0且没有等待接收的 Goroutine)或(buffer 已经满了)就直接返回false if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) || (c.dataqsiz > 0 && c.qcount == c.dataqsiz)) { returnfalse }
var t0 int64 if blockprofilerate > 0 { t0 = cputicks() }
lock(&c.lock)
// 检查 Channel 是否关闭 if c.closed != 0 { unlock(&c.lock) panic(plainError("send on closed channel")) }
// 该操作是非阻塞的就直接返回 false if !block { unlock(&c.lock) returnfalse }
// 接下来就是阻塞该 Goroutine,等待有 Goroutine 来读数据 gp := getg() mysg := acquireSudog() mysg.releasetime = 0 if t0 != 0 { mysg.releasetime = -1 } mysg.elem = ep mysg.waitlink = nil mysg.g = gp mysg.isSelect = false mysg.c = c gp.waiting = mysg gp.param = nil // 将该 Goroutine 压入等待发送的队列 c.sendq.enqueue(mysg) // 将当前 goroutine 挂起,会在下文的 recv 函数里面被唤醒 goparkunlock(&c.lock, waitReasonChanSend, traceEvGoBlockSend, 3) // Ensure the value being sent is kept alive until the // receiver copies it out. The sudog has a pointer to the // stack object, but sudogs aren't considered as roots of the // stack tracer. KeepAlive(ep)
// 唤醒之后做一些收尾工作 if mysg != gp.waiting { throw("G waiting list is corrupted") } gp.waiting = nil if gp.param == nil { if c.closed == 0 { throw("chansend: spurious wakeup") } panic(plainError("send on closed channel")) } gp.param = nil if mysg.releasetime > 0 { blockevent(mysg.releasetime-t0, 2) } mysg.c = nil releaseSudog(mysg) returntrue }